From 8d4e4a2c7891be3c0786e9d3c43ee74011cac5b6 Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Fri, 19 Sep 2025 01:34:03 -0400 Subject: [PATCH 01/10] fix: do not generate jar files for projects that do not publish them --- gradle/java-config.gradle | 5 +++ gradle/publish-config.gradle | 41 +++++++++++++--------- grails-forge/gradle/java-config.gradle | 5 +++ grails-forge/gradle/publish-config.gradle | 36 ++++++++++--------- grails-gradle/gradle/java-config.gradle | 5 +++ grails-gradle/gradle/publish-config.gradle | 2 +- 6 files changed, 59 insertions(+), 35 deletions(-) diff --git a/gradle/java-config.gradle b/gradle/java-config.gradle index 46cb1956add..46b05c1914c 100644 --- a/gradle/java-config.gradle +++ b/gradle/java-config.gradle @@ -44,6 +44,11 @@ tasks.withType(GroovyCompile).configureEach { // Grails determines the grails version via the META-INF/MANIFEST.MF file // Note: we exclude attributes such as Built-By, Build-Jdk, Created-By to ensure the build is reproducible. tasks.withType(Jar).configureEach { + if (project.findProperty('skipJavaComponent')) { + it.enabled = false + return + } + manifest.attributes( 'Implementation-Title': 'Apache Grails', 'Implementation-Version': grailsVersion, diff --git a/gradle/publish-config.gradle b/gradle/publish-config.gradle index 6552273cc68..b65793ad47d 100644 --- a/gradle/publish-config.gradle +++ b/gradle/publish-config.gradle @@ -30,29 +30,36 @@ extensions.configure(GrailsPublishExtension) { it.developers = findProperty('pomDevelopers') as Map ?: [graemerocher: 'Graeme Rocher'] it.pomCustomization = findProperty('pomCustomization') as Closure it.publishTestSources = findProperty('pomPublishTestSources') ?: false - it.testRepositoryPath = rootProject.layout.buildDirectory.dir('local-maven') + it.testRepositoryPath = findProperty('skipJavaComponent') ? null : rootProject.layout.projectDirectory.dir('../build/local-maven') } -tasks.withType(Jar).configureEach { - if(it.archiveClassifier.getOrNull() != 'javadoc') { - from(rootProject.layout.projectDirectory.file('DISCLAIMER')) { - into('META-INF') - } +if (findProperty('skipJavaComponent')) { + // since the publish plugin won't register + tasks.register('publishAllPublicationsToTestCaseMavenRepoRepository') +} - def projectLicense = layout.projectDirectory.file('src/main/resources/META-INF/LICENSE') - if (!projectLicense.asFile.exists()) { - def basicLicense = rootProject.layout.projectDirectory.file('licenses/LICENSE-Apache-2.0.txt') - from(basicLicense) { +if (!findProperty('skipJavaComponent')) { + tasks.withType(Jar).configureEach { + if (it.archiveClassifier.getOrNull() != 'javadoc') { + from(rootProject.layout.projectDirectory.file('DISCLAIMER')) { into('META-INF') - rename { 'LICENSE' } } - } - def projectNotice = layout.projectDirectory.file('src/main/resources/META-INF/NOTICE') - if (!projectNotice.asFile.exists()) { - def basicNotice = rootProject.layout.projectDirectory.file('grails-core/src/main/resources/META-INF/NOTICE') - from(basicNotice) { - into('META-INF') + def projectLicense = layout.projectDirectory.file('src/main/resources/META-INF/LICENSE') + if (!projectLicense.asFile.exists()) { + def basicLicense = rootProject.layout.projectDirectory.file('licenses/LICENSE-Apache-2.0.txt') + from(basicLicense) { + into('META-INF') + rename { 'LICENSE' } + } + } + + def projectNotice = layout.projectDirectory.file('src/main/resources/META-INF/NOTICE') + if (!projectNotice.asFile.exists()) { + def basicNotice = rootProject.layout.projectDirectory.file('grails-core/src/main/resources/META-INF/NOTICE') + from(basicNotice) { + into('META-INF') + } } } } diff --git a/grails-forge/gradle/java-config.gradle b/grails-forge/gradle/java-config.gradle index dab7db9cba3..3e43348098c 100644 --- a/grails-forge/gradle/java-config.gradle +++ b/grails-forge/gradle/java-config.gradle @@ -48,6 +48,11 @@ tasks.withType(GroovyCompile).configureEach { // Grails determines the grails version via the META-INF/MANIFEST.MF file // Note: we exclude attributes such as Built-By, Build-Jdk, Created-By to ensure the build is reproducible. tasks.withType(Jar).configureEach { + if (project.findProperty('skipJavaComponent')) { + it.enabled = false + return + } + manifest.attributes( 'Implementation-Title': 'Apache Grails', 'Implementation-Version': projectVersion, diff --git a/grails-forge/gradle/publish-config.gradle b/grails-forge/gradle/publish-config.gradle index df16ad89be4..a30d0710c81 100644 --- a/grails-forge/gradle/publish-config.gradle +++ b/grails-forge/gradle/publish-config.gradle @@ -33,26 +33,28 @@ extensions.configure(GrailsPublishExtension) { it.publishTestSources = findProperty('pomPublishTestSources') ?: false } -tasks.withType(Jar).configureEach { - if(it.archiveClassifier.getOrNull() != 'javadoc') { - from(rootProject.layout.projectDirectory.file('../DISCLAIMER')) { - into('META-INF') - } - - def projectLicense = layout.projectDirectory.file('src/main/resources/META-INF/LICENSE') - if (!projectLicense.asFile.exists()) { - def basicLicense = rootProject.layout.projectDirectory.file('../licenses/LICENSE-Apache-2.0.txt') - from(basicLicense) { +if (!project.findProperty('skipJavaComponent')) { + tasks.withType(Jar).configureEach { + if (it.archiveClassifier.getOrNull() != 'javadoc') { + from(rootProject.layout.projectDirectory.file('../DISCLAIMER')) { into('META-INF') - rename { 'LICENSE' } } - } - def projectNotice = layout.projectDirectory.file('src/main/resources/META-INF/NOTICE') - if (!projectNotice.asFile.exists()) { - def basicNotice = rootProject.layout.projectDirectory.file('../grails-core/src/main/resources/META-INF/NOTICE') - from(basicNotice) { - into('META-INF') + def projectLicense = layout.projectDirectory.file('src/main/resources/META-INF/LICENSE') + if (!projectLicense.asFile.exists()) { + def basicLicense = rootProject.layout.projectDirectory.file('../licenses/LICENSE-Apache-2.0.txt') + from(basicLicense) { + into('META-INF') + rename { 'LICENSE' } + } + } + + def projectNotice = layout.projectDirectory.file('src/main/resources/META-INF/NOTICE') + if (!projectNotice.asFile.exists()) { + def basicNotice = rootProject.layout.projectDirectory.file('../grails-core/src/main/resources/META-INF/NOTICE') + from(basicNotice) { + into('META-INF') + } } } } diff --git a/grails-gradle/gradle/java-config.gradle b/grails-gradle/gradle/java-config.gradle index c5d538865a9..cab40a97ad1 100644 --- a/grails-gradle/gradle/java-config.gradle +++ b/grails-gradle/gradle/java-config.gradle @@ -58,6 +58,11 @@ tasks.withType(GroovyCompile).configureEach { // Grails determines the grails version via the META-INF/MANIFEST.MF file // Note: we exclude attributes such as Built-By, Build-Jdk, Created-By to ensure the build is reproducible. tasks.withType(Jar).configureEach { + if (project.findProperty('skipJavaComponent')) { + it.enabled = false + return + } + manifest.attributes( 'Implementation-Title': 'Apache Grails', 'Implementation-Version': projectVersion, diff --git a/grails-gradle/gradle/publish-config.gradle b/grails-gradle/gradle/publish-config.gradle index 59ab250b274..5feed2c8958 100644 --- a/grails-gradle/gradle/publish-config.gradle +++ b/grails-gradle/gradle/publish-config.gradle @@ -32,7 +32,7 @@ extensions.configure(GrailsPublishExtension) { it.developers = findProperty('pomDevelopers') as Map ?: [graemerocher: 'Graeme Rocher', jeffscottbrown: 'Jeff Scott Brown', puneetbehl: 'Puneet Behl'] it.pomCustomization = findProperty('pomCustomization') as Closure it.publishTestSources = findProperty('pomPublishTestSources') ?: false - it.testRepositoryPath = rootProject.layout.projectDirectory.dir('../build/local-maven') + it.testRepositoryPath = findProperty('skipJavaComponent') ? null : rootProject.layout.projectDirectory.dir('../build/local-maven') it.publicationName = findProperty('pomMavenPublicationName') ?: 'maven' it.addComponents = project.name != 'grails-gradle-plugins' } From 775b356a6de57cda3301b268665ca15164aed346 Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Fri, 19 Sep 2025 01:44:13 -0400 Subject: [PATCH 02/10] feature: generate reproducible sboms that are stored in published in any published binary jar under META-INF/sbom.json --- buildSrc/build.gradle | 1 + gradle.properties | 2 + gradle/sbom-config.gradle | 143 ++++++++++++++++++ grails-async/core/build.gradle | 1 + grails-async/gpars/build.gradle | 1 + grails-async/plugin/build.gradle | 1 + grails-async/rxjava/build.gradle | 1 + grails-async/rxjava2/build.gradle | 1 + grails-async/rxjava3/build.gradle | 1 + grails-bom/build.gradle | 1 + grails-bootstrap/build.gradle | 1 + grails-cache/build.gradle | 1 + grails-codecs-core/build.gradle | 1 + grails-codecs/build.gradle | 1 + grails-common/build.gradle | 1 + grails-console/build.gradle | 1 + grails-controllers/build.gradle | 1 + grails-converters/build.gradle | 1 + grails-core/build.gradle | 1 + .../boot-plugin/build.gradle | 1 + grails-data-hibernate5/core/build.gradle | 1 + .../dbmigration/build.gradle | 1 + .../grails-plugin/build.gradle | 1 + grails-data-mongodb/boot-plugin/build.gradle | 1 + grails-data-mongodb/bson/build.gradle | 1 + grails-data-mongodb/core/build.gradle | 1 + grails-data-mongodb/ext/build.gradle | 1 + .../grails-plugin/build.gradle | 1 + .../gson-templates/build.gradle | 1 + grails-data-simple/build.gradle | 1 + grails-databinding-core/build.gradle | 1 + grails-databinding/build.gradle | 1 + grails-datamapping-async/build.gradle | 1 + grails-datamapping-core-test/build.gradle | 1 + grails-datamapping-core/build.gradle | 1 + grails-datamapping-rx/build.gradle | 1 + grails-datamapping-support/build.gradle | 1 + grails-datamapping-tck/build.gradle | 1 + grails-datamapping-validation/build.gradle | 1 + grails-datasource/build.gradle | 1 + grails-datastore-async/build.gradle | 1 + grails-datastore-core/build.gradle | 1 + grails-datastore-web/build.gradle | 1 + grails-dependencies/assets/build.gradle | 1 + grails-dependencies/starter-web/build.gradle | 1 + grails-dependencies/test/build.gradle | 1 + grails-domain-class/build.gradle | 1 + grails-encoder/build.gradle | 1 + grails-events/compat/build.gradle | 1 + grails-events/core/build.gradle | 1 + grails-events/gpars/build.gradle | 1 + grails-events/plugin/build.gradle | 1 + grails-events/rxjava/build.gradle | 1 + grails-events/rxjava2/build.gradle | 1 + grails-events/rxjava3/build.gradle | 1 + grails-events/spring/build.gradle | 1 + grails-events/transforms/build.gradle | 1 + grails-fields/build.gradle | 1 + grails-forge/buildSrc/build.gradle | 1 + grails-forge/gradle/sbom-config.gradle | 143 ++++++++++++++++++ grails-forge/grails-cli/build.gradle | 1 + grails-forge/grails-forge-cli/build.gradle | 1 + grails-forge/grails-forge-core/build.gradle | 1 + grails-geb/build.gradle | 1 + grails-gradle/bom/build.gradle | 1 + grails-gradle/buildSrc/build.gradle | 1 + grails-gradle/common/build.gradle | 1 + grails-gradle/docs-core/build.gradle | 2 + grails-gradle/gradle/sbom-config.gradle | 143 ++++++++++++++++++ grails-gradle/model/build.gradle | 2 + grails-gradle/plugins/build.gradle | 1 + grails-gradle/tasks/build.gradle | 2 + grails-gsp/core/build.gradle | 1 + grails-gsp/grails-layout/build.gradle | 1 + grails-gsp/grails-sitemesh3/build.gradle | 1 + grails-gsp/grails-taglib/build.gradle | 1 + grails-gsp/grails-web-gsp-taglib/build.gradle | 1 + grails-gsp/grails-web-gsp/build.gradle | 1 + grails-gsp/grails-web-jsp/build.gradle | 1 + grails-gsp/grails-web-taglib/build.gradle | 1 + grails-gsp/plugin/build.gradle | 1 + grails-i18n/build.gradle | 1 + grails-interceptors/build.gradle | 1 + grails-logging/build.gradle | 1 + grails-micronaut/build.gradle | 1 + grails-mimetypes/build.gradle | 1 + grails-profiles/base/build.gradle | 1 + grails-profiles/plugin/build.gradle | 1 + grails-profiles/profile/build.gradle | 1 + grails-profiles/rest-api-plugin/build.gradle | 1 + grails-profiles/rest-api/build.gradle | 1 + grails-profiles/web-plugin/build.gradle | 1 + grails-profiles/web/build.gradle | 1 + grails-rest-transforms/build.gradle | 1 + grails-scaffolding/build.gradle | 1 + grails-services/build.gradle | 1 + grails-shell-cli/build.gradle | 1 + grails-spring/build.gradle | 1 + grails-test-core/build.gradle | 1 + grails-testing-support-core/build.gradle | 1 + .../build.gradle | 1 + grails-testing-support-mongodb/build.gradle | 1 + .../build.gradle | 1 + grails-testing-support-web/build.gradle | 1 + grails-url-mappings/build.gradle | 1 + grails-validation/build.gradle | 1 + grails-views-core/build.gradle | 1 + grails-views-gson/build.gradle | 1 + grails-views-markup/build.gradle | 1 + grails-web-boot/build.gradle | 1 + grails-web-common/build.gradle | 1 + grails-web-core/build.gradle | 1 + grails-web-databinding/build.gradle | 1 + grails-web-mvc/build.gradle | 1 + grails-web-url-mappings/build.gradle | 1 + grails-wrapper/build.gradle | 1 + 116 files changed, 546 insertions(+) create mode 100644 gradle/sbom-config.gradle create mode 100644 grails-forge/gradle/sbom-config.gradle create mode 100644 grails-gradle/gradle/sbom-config.gradle diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index ca7f53f7f20..0e5cd7f88f2 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -73,5 +73,6 @@ dependencies { implementation 'org.asciidoctor:asciidoctor-gradle-jvm' implementation 'org.springframework.boot:spring-boot-gradle-plugin' implementation "org.nosphere.apache.rat:org.nosphere.apache.rat.gradle.plugin:${gradleProperties.apacheRatVersion}" + implementation "org.cyclonedx.bom:org.cyclonedx.bom.gradle.plugin:${gradleProperties.gradleCycloneDxPluginVersion}" implementation "org.gradle.crypto.checksum:org.gradle.crypto.checksum.gradle.plugin:${gradleProperties.gradleChecksumPluginVersion}" } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 986a4d5f1ed..83805e20bbc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -47,6 +47,8 @@ yakworksHibernateGroovyProxyVersion=1.1 # Build dependency versions not managed by BOMs apacheRatVersion=0.8.1 gradleChecksumPluginVersion=1.4.0 +# note: the cyclonedx 3.0.0-alpha-1 still does not set the project correctly, so we must use the older version +gradleCycloneDxPluginVersion=2.4.0 gradleLicensePluginVersion=0.16.1 # micronaut libraries not in the bom due to the potential for spring mismatches diff --git a/gradle/sbom-config.gradle b/gradle/sbom-config.gradle new file mode 100644 index 00000000000..779e6ac1f34 --- /dev/null +++ b/gradle/sbom-config.gradle @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +import org.cyclonedx.gradle.CycloneDxTask +import org.cyclonedx.model.ExternalReference +import org.cyclonedx.model.LicenseChoice +import org.cyclonedx.model.License +import org.cyclonedx.model.OrganizationalContact +import org.cyclonedx.model.OrganizationalEntity +import org.cyclonedx.model.Component + +import java.nio.charset.StandardCharsets +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit + +apply plugin: 'org.cyclonedx.bom' + +project.ext.setProperty('sbomOutputLocation', project.layout.buildDirectory.file("${findProperty('pomArtifactId') ?: project.name}-${projectVersion}-sbom.json")) + +def sbomTask = tasks.named('cyclonedxBom', CycloneDxTask) +sbomTask.configure { CycloneDxTask it -> + // the 2.x version of Cyclonedx uses a legacy syntax & helpers for setting inputs so the syntax below + // is required until the 3.x version is GA + it.setProjectType(findProperty('sbomProjectType') ?: Component.Type.FRAMEWORK.name()) + it.@componentName.set(findProperty('pomArtifactId') ?: project.name) + it.@organizationalEntity.set(new OrganizationalEntity().tap { + name = 'Apache Software Foundation' + urls = ['https://www.apache.org/', 'https://security.apache.org/'] + addContact(new OrganizationalContact().tap { + name = 'Apache Grails Development Team' + email = 'dev@grails.apache.org' + }) + }) + it.@licenseChoice.set(new LicenseChoice().tap { + addLicense(new License().tap { + name = 'Apache-2.0' + url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' + }) + }) + + it.@externalReferences.set([ + new ExternalReference().tap { + url = 'https://grails.apache.org/' + type = ExternalReference.Type.WEBSITE + } + ]) + + // sboms are published for the purposes of vulnerability analysis so only include the runtime classpath + it.@includeConfigs.set(['runtimeClasspath']) + it.@skipConfigs.set(['compileClasspath', 'testRuntimeClasspath']) + + // disable xml output + it.xmlOutput.unsetConvention() + + def sbomOutputLocation = findProperty('sbomOutputLocation') + it.jsonOutput.set(sbomOutputLocation.get()) + it.outputs.file(sbomOutputLocation) + + // cyclonedx does not support "choosing" the license placed in the sbom + // see: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/16 + it.doLast { + def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0'] + + def pickLicense = { List licenses -> + if (!(licenses instanceof List) || licenses.isEmpty()) { + return null + } + + def licenseIds = licenses.findAll { it instanceof Map && it.license instanceof Map && it.license.id } + def foundLicense = preferredLicenses.find { p -> licenseIds.any { it.license.id == p } } + if (foundLicense) { + return licenseIds.find { it.license.id == foundLicense } + } + + licenses[0] // pick the first one found + } + + def rewriteSbom = { File f -> + def bom = new JsonSlurper().parse(f) + + // timestamp is not reproducible: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/292 + bom.metadata.timestamp = DateTimeFormatter.ISO_INSTANT.format(buildDate.truncatedTo(ChronoUnit.SECONDS)) + + // components[*].licenses + def comps = (bom instanceof Map && bom.components instanceof List) ? bom.components : [] + comps.each { c -> + if (c instanceof Map && c.licenses instanceof List && !c.licenses.isEmpty()) { + def chosen = pickLicense(c.licenses as List) + if (chosen != null) { + c.licenses = [chosen] + } + } + } + + // force the serialNumber to be reproducible by removing it & recalculating + bom.serialNumber = '' + String withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom)) + def uuid = UUID.nameUUIDFromBytes(withOutSerial.getBytes(StandardCharsets.UTF_8.name())) + def urn = "urn:uuid:${uuid.toString()}" as String + bom.serialNumber = urn + + f.setText(JsonOutput.prettyPrint(JsonOutput.toJson(bom)), StandardCharsets.UTF_8.name()) + + logger.info('Rewrote JSON SBOM ({}) to pick preferred license', project.relativePath(f)) + } + + sbomOutputLocation.get().with { rewriteSbom(it.asFile) } + } +} + +// for now, we only publish the sbom inside of the binary jar (our bom projects won't have a jar file) +pluginManager.withPlugin('java') { + if (!project.findProperty('skipJavaComponent')) { + tasks.named('jar', Jar).configure { Jar jar -> + jar.dependsOn tasks.named('cyclonedxBom') + + jar.from(findProperty('sbomOutputLocation')) { + into('META-INF') + rename { + 'sbom.json' + } + } + } + } +} diff --git a/grails-async/core/build.gradle b/grails-async/core/build.gradle index a9f35631a1e..232e9f93b1d 100644 --- a/grails-async/core/build.gradle +++ b/grails-async/core/build.gradle @@ -50,5 +50,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-async/gpars/build.gradle b/grails-async/gpars/build.gradle index daeb08a9222..029f1010eb6 100644 --- a/grails-async/gpars/build.gradle +++ b/grails-async/gpars/build.gradle @@ -50,5 +50,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-async/plugin/build.gradle b/grails-async/plugin/build.gradle index f4078b32cfe..e6ce2a446b1 100644 --- a/grails-async/plugin/build.gradle +++ b/grails-async/plugin/build.gradle @@ -60,5 +60,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-async/rxjava/build.gradle b/grails-async/rxjava/build.gradle index 00a583c67fb..38c28e84b27 100644 --- a/grails-async/rxjava/build.gradle +++ b/grails-async/rxjava/build.gradle @@ -46,5 +46,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-async/rxjava2/build.gradle b/grails-async/rxjava2/build.gradle index 9f1cff3f12a..ddcaf3bb718 100644 --- a/grails-async/rxjava2/build.gradle +++ b/grails-async/rxjava2/build.gradle @@ -46,5 +46,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-async/rxjava3/build.gradle b/grails-async/rxjava3/build.gradle index 2cd8942622e..7bd5445bf70 100644 --- a/grails-async/rxjava3/build.gradle +++ b/grails-async/rxjava3/build.gradle @@ -46,5 +46,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-bom/build.gradle b/grails-bom/build.gradle index 3824ea2c98f..88b8d7fb96f 100644 --- a/grails-bom/build.gradle +++ b/grails-bom/build.gradle @@ -202,4 +202,5 @@ ext { apply { from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } diff --git a/grails-bootstrap/build.gradle b/grails-bootstrap/build.gradle index 15017ca16b2..a6ae77322a2 100644 --- a/grails-bootstrap/build.gradle +++ b/grails-bootstrap/build.gradle @@ -84,5 +84,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-cache/build.gradle b/grails-cache/build.gradle index 89e5a3e35e3..0b16f0a7744 100644 --- a/grails-cache/build.gradle +++ b/grails-cache/build.gradle @@ -63,6 +63,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } diff --git a/grails-codecs-core/build.gradle b/grails-codecs-core/build.gradle index 662060b41dd..ac7e542a381 100644 --- a/grails-codecs-core/build.gradle +++ b/grails-codecs-core/build.gradle @@ -57,5 +57,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-codecs/build.gradle b/grails-codecs/build.gradle index 26eab116b06..0815e1cf1ef 100644 --- a/grails-codecs/build.gradle +++ b/grails-codecs/build.gradle @@ -67,5 +67,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-common/build.gradle b/grails-common/build.gradle index e30f95621aa..378584eef77 100644 --- a/grails-common/build.gradle +++ b/grails-common/build.gradle @@ -58,5 +58,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-console/build.gradle b/grails-console/build.gradle index a83ac41a461..55d6a5ed6ac 100644 --- a/grails-console/build.gradle +++ b/grails-console/build.gradle @@ -70,4 +70,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-controllers/build.gradle b/grails-controllers/build.gradle index 3d88b08a4ef..d301da5c835 100644 --- a/grails-controllers/build.gradle +++ b/grails-controllers/build.gradle @@ -77,5 +77,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-converters/build.gradle b/grails-converters/build.gradle index 182b6d6ed7f..7012f9dedec 100644 --- a/grails-converters/build.gradle +++ b/grails-converters/build.gradle @@ -76,5 +76,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-core/build.gradle b/grails-core/build.gradle index 79a030fc6e1..cc8f5277f71 100644 --- a/grails-core/build.gradle +++ b/grails-core/build.gradle @@ -95,5 +95,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-data-hibernate5/boot-plugin/build.gradle b/grails-data-hibernate5/boot-plugin/build.gradle index 52e6497737d..3e9993d2346 100644 --- a/grails-data-hibernate5/boot-plugin/build.gradle +++ b/grails-data-hibernate5/boot-plugin/build.gradle @@ -62,5 +62,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/hibernate5-test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-data-hibernate5/core/build.gradle b/grails-data-hibernate5/core/build.gradle index db4d955201b..9085f64c265 100644 --- a/grails-data-hibernate5/core/build.gradle +++ b/grails-data-hibernate5/core/build.gradle @@ -92,6 +92,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/hibernate5-test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-data-tck-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-data-hibernate5/dbmigration/build.gradle b/grails-data-hibernate5/dbmigration/build.gradle index 790cc41c23a..792e0683f8d 100644 --- a/grails-data-hibernate5/dbmigration/build.gradle +++ b/grails-data-hibernate5/dbmigration/build.gradle @@ -109,6 +109,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/hibernate5-test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } diff --git a/grails-data-hibernate5/grails-plugin/build.gradle b/grails-data-hibernate5/grails-plugin/build.gradle index 892d01db72f..8b05552ffb0 100644 --- a/grails-data-hibernate5/grails-plugin/build.gradle +++ b/grails-data-hibernate5/grails-plugin/build.gradle @@ -88,6 +88,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/hibernate5-test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } \ No newline at end of file diff --git a/grails-data-mongodb/boot-plugin/build.gradle b/grails-data-mongodb/boot-plugin/build.gradle index 84074347a87..d0e5ee88053 100644 --- a/grails-data-mongodb/boot-plugin/build.gradle +++ b/grails-data-mongodb/boot-plugin/build.gradle @@ -107,5 +107,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/mongodb-test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } \ No newline at end of file diff --git a/grails-data-mongodb/bson/build.gradle b/grails-data-mongodb/bson/build.gradle index dda142f5671..dcd3ea88393 100644 --- a/grails-data-mongodb/bson/build.gradle +++ b/grails-data-mongodb/bson/build.gradle @@ -101,5 +101,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/mongodb-test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } \ No newline at end of file diff --git a/grails-data-mongodb/core/build.gradle b/grails-data-mongodb/core/build.gradle index 6c89d156661..1a46cbc7412 100644 --- a/grails-data-mongodb/core/build.gradle +++ b/grails-data-mongodb/core/build.gradle @@ -154,6 +154,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/mongodb-forked-test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-data-tck-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } \ No newline at end of file diff --git a/grails-data-mongodb/ext/build.gradle b/grails-data-mongodb/ext/build.gradle index e9ba4cd7626..7a91a3a342a 100644 --- a/grails-data-mongodb/ext/build.gradle +++ b/grails-data-mongodb/ext/build.gradle @@ -90,5 +90,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/mongodb-test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-data-mongodb/grails-plugin/build.gradle b/grails-data-mongodb/grails-plugin/build.gradle index 4d633438027..44aa6964181 100644 --- a/grails-data-mongodb/grails-plugin/build.gradle +++ b/grails-data-mongodb/grails-plugin/build.gradle @@ -132,6 +132,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/mongodb-test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } diff --git a/grails-data-mongodb/gson-templates/build.gradle b/grails-data-mongodb/gson-templates/build.gradle index d821432f531..77d121996e7 100644 --- a/grails-data-mongodb/gson-templates/build.gradle +++ b/grails-data-mongodb/gson-templates/build.gradle @@ -72,5 +72,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/mongodb-test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-data-simple/build.gradle b/grails-data-simple/build.gradle index df43a6fef72..d83119f62bd 100644 --- a/grails-data-simple/build.gradle +++ b/grails-data-simple/build.gradle @@ -95,6 +95,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-databinding-core/build.gradle b/grails-databinding-core/build.gradle index 28a0551fbb3..6dea9c0bbaf 100644 --- a/grails-databinding-core/build.gradle +++ b/grails-databinding-core/build.gradle @@ -60,5 +60,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-databinding/build.gradle b/grails-databinding/build.gradle index 6261cef74a6..8de6190d762 100644 --- a/grails-databinding/build.gradle +++ b/grails-databinding/build.gradle @@ -73,5 +73,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-datamapping-async/build.gradle b/grails-datamapping-async/build.gradle index 9e707b50bbb..da690bb3aa2 100644 --- a/grails-datamapping-async/build.gradle +++ b/grails-datamapping-async/build.gradle @@ -77,5 +77,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-datamapping-core-test/build.gradle b/grails-datamapping-core-test/build.gradle index e55f775f238..62807a753cd 100644 --- a/grails-datamapping-core-test/build.gradle +++ b/grails-datamapping-core-test/build.gradle @@ -136,5 +136,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-data-tck-config.gradle') } diff --git a/grails-datamapping-core/build.gradle b/grails-datamapping-core/build.gradle index 8295b2e69e6..f0b29c7a66a 100644 --- a/grails-datamapping-core/build.gradle +++ b/grails-datamapping-core/build.gradle @@ -121,5 +121,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-datamapping-rx/build.gradle b/grails-datamapping-rx/build.gradle index fcaf4b5b6d9..071b73757c2 100644 --- a/grails-datamapping-rx/build.gradle +++ b/grails-datamapping-rx/build.gradle @@ -68,4 +68,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } diff --git a/grails-datamapping-support/build.gradle b/grails-datamapping-support/build.gradle index b4cec37d891..f7104cabf5d 100644 --- a/grails-datamapping-support/build.gradle +++ b/grails-datamapping-support/build.gradle @@ -122,5 +122,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-datamapping-tck/build.gradle b/grails-datamapping-tck/build.gradle index b6ace68d757..efaf3a89606 100644 --- a/grails-datamapping-tck/build.gradle +++ b/grails-datamapping-tck/build.gradle @@ -117,5 +117,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') // do NOT do test configuration here, this is a TCK module } diff --git a/grails-datamapping-validation/build.gradle b/grails-datamapping-validation/build.gradle index cabf5e7304f..41715343dfb 100644 --- a/grails-datamapping-validation/build.gradle +++ b/grails-datamapping-validation/build.gradle @@ -89,6 +89,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-datasource/build.gradle b/grails-datasource/build.gradle index 969a788109d..6fdaed79907 100644 --- a/grails-datasource/build.gradle +++ b/grails-datasource/build.gradle @@ -74,5 +74,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-datastore-async/build.gradle b/grails-datastore-async/build.gradle index 6ad2d35cb47..bebb9e40f39 100644 --- a/grails-datastore-async/build.gradle +++ b/grails-datastore-async/build.gradle @@ -64,5 +64,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-datastore-core/build.gradle b/grails-datastore-core/build.gradle index 87a2d913455..43e34459320 100644 --- a/grails-datastore-core/build.gradle +++ b/grails-datastore-core/build.gradle @@ -101,6 +101,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-datastore-web/build.gradle b/grails-datastore-web/build.gradle index 995de819b8a..1ab8c3d8289 100644 --- a/grails-datastore-web/build.gradle +++ b/grails-datastore-web/build.gradle @@ -91,6 +91,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-dependencies/assets/build.gradle b/grails-dependencies/assets/build.gradle index 3828cba1828..5170e847b79 100644 --- a/grails-dependencies/assets/build.gradle +++ b/grails-dependencies/assets/build.gradle @@ -55,6 +55,7 @@ apply { // java-configuration must be applied first since tasks are now lazy registered from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } // these must be after the above applies because they opt out of the defaults created in those files diff --git a/grails-dependencies/starter-web/build.gradle b/grails-dependencies/starter-web/build.gradle index 45cb6206fde..d342b195bbd 100644 --- a/grails-dependencies/starter-web/build.gradle +++ b/grails-dependencies/starter-web/build.gradle @@ -79,6 +79,7 @@ apply { // java-configuration must be applied first since tasks are now lazy registered from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } // these must be after the above applies because they opt out of the defaults created in those files diff --git a/grails-dependencies/test/build.gradle b/grails-dependencies/test/build.gradle index c6d539f3c42..2fd2b1b5aea 100644 --- a/grails-dependencies/test/build.gradle +++ b/grails-dependencies/test/build.gradle @@ -57,6 +57,7 @@ apply { // java-configuration must be applied first since tasks are now lazy registered from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } // these must be after the above applies because they opt out of the defaults created in those files diff --git a/grails-domain-class/build.gradle b/grails-domain-class/build.gradle index bae1bbd4a53..da3c19dd994 100644 --- a/grails-domain-class/build.gradle +++ b/grails-domain-class/build.gradle @@ -80,5 +80,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-encoder/build.gradle b/grails-encoder/build.gradle index 90ad28fbcc4..7534aa64ac0 100644 --- a/grails-encoder/build.gradle +++ b/grails-encoder/build.gradle @@ -61,5 +61,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-events/compat/build.gradle b/grails-events/compat/build.gradle index d9385c97dcd..05c2bc6266f 100644 --- a/grails-events/compat/build.gradle +++ b/grails-events/compat/build.gradle @@ -41,4 +41,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-events/core/build.gradle b/grails-events/core/build.gradle index 6e72243d77f..1197a281030 100644 --- a/grails-events/core/build.gradle +++ b/grails-events/core/build.gradle @@ -47,5 +47,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-events/gpars/build.gradle b/grails-events/gpars/build.gradle index 6db7a2796be..91fc424c6cf 100644 --- a/grails-events/gpars/build.gradle +++ b/grails-events/gpars/build.gradle @@ -48,5 +48,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-events/plugin/build.gradle b/grails-events/plugin/build.gradle index cdd78d8d50e..a537f25922c 100644 --- a/grails-events/plugin/build.gradle +++ b/grails-events/plugin/build.gradle @@ -54,5 +54,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-events/rxjava/build.gradle b/grails-events/rxjava/build.gradle index f5421639f95..40685b5cbc3 100644 --- a/grails-events/rxjava/build.gradle +++ b/grails-events/rxjava/build.gradle @@ -51,5 +51,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-events/rxjava2/build.gradle b/grails-events/rxjava2/build.gradle index da07f0bad5b..245e965371e 100644 --- a/grails-events/rxjava2/build.gradle +++ b/grails-events/rxjava2/build.gradle @@ -47,5 +47,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-events/rxjava3/build.gradle b/grails-events/rxjava3/build.gradle index 836342bd77f..7c88e271f3d 100644 --- a/grails-events/rxjava3/build.gradle +++ b/grails-events/rxjava3/build.gradle @@ -47,5 +47,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-events/spring/build.gradle b/grails-events/spring/build.gradle index 94bcb756993..6d58e8837c0 100644 --- a/grails-events/spring/build.gradle +++ b/grails-events/spring/build.gradle @@ -47,5 +47,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-events/transforms/build.gradle b/grails-events/transforms/build.gradle index 8312249c118..3b63b115405 100644 --- a/grails-events/transforms/build.gradle +++ b/grails-events/transforms/build.gradle @@ -64,5 +64,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-fields/build.gradle b/grails-fields/build.gradle index 98bd1f6af41..9546dfd1431 100644 --- a/grails-fields/build.gradle +++ b/grails-fields/build.gradle @@ -63,6 +63,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } \ No newline at end of file diff --git a/grails-forge/buildSrc/build.gradle b/grails-forge/buildSrc/build.gradle index 4684886d05a..70b37a785d1 100644 --- a/grails-forge/buildSrc/build.gradle +++ b/grails-forge/buildSrc/build.gradle @@ -99,6 +99,7 @@ dependencies { implementation "org.gradle.crypto.checksum:org.gradle.crypto.checksum.gradle.plugin:$gradleChecksumPluginVersion" implementation "org.apache.ant:ant:$antVersion" implementation "org.apache.grails.gradle:grails-gradle-common:$projectVersion" + implementation "org.cyclonedx.bom:org.cyclonedx.bom.gradle.plugin:${rootProperties.gradleCycloneDxPluginVersion}" } gradlePlugin { diff --git a/grails-forge/gradle/sbom-config.gradle b/grails-forge/gradle/sbom-config.gradle new file mode 100644 index 00000000000..779e6ac1f34 --- /dev/null +++ b/grails-forge/gradle/sbom-config.gradle @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +import org.cyclonedx.gradle.CycloneDxTask +import org.cyclonedx.model.ExternalReference +import org.cyclonedx.model.LicenseChoice +import org.cyclonedx.model.License +import org.cyclonedx.model.OrganizationalContact +import org.cyclonedx.model.OrganizationalEntity +import org.cyclonedx.model.Component + +import java.nio.charset.StandardCharsets +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit + +apply plugin: 'org.cyclonedx.bom' + +project.ext.setProperty('sbomOutputLocation', project.layout.buildDirectory.file("${findProperty('pomArtifactId') ?: project.name}-${projectVersion}-sbom.json")) + +def sbomTask = tasks.named('cyclonedxBom', CycloneDxTask) +sbomTask.configure { CycloneDxTask it -> + // the 2.x version of Cyclonedx uses a legacy syntax & helpers for setting inputs so the syntax below + // is required until the 3.x version is GA + it.setProjectType(findProperty('sbomProjectType') ?: Component.Type.FRAMEWORK.name()) + it.@componentName.set(findProperty('pomArtifactId') ?: project.name) + it.@organizationalEntity.set(new OrganizationalEntity().tap { + name = 'Apache Software Foundation' + urls = ['https://www.apache.org/', 'https://security.apache.org/'] + addContact(new OrganizationalContact().tap { + name = 'Apache Grails Development Team' + email = 'dev@grails.apache.org' + }) + }) + it.@licenseChoice.set(new LicenseChoice().tap { + addLicense(new License().tap { + name = 'Apache-2.0' + url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' + }) + }) + + it.@externalReferences.set([ + new ExternalReference().tap { + url = 'https://grails.apache.org/' + type = ExternalReference.Type.WEBSITE + } + ]) + + // sboms are published for the purposes of vulnerability analysis so only include the runtime classpath + it.@includeConfigs.set(['runtimeClasspath']) + it.@skipConfigs.set(['compileClasspath', 'testRuntimeClasspath']) + + // disable xml output + it.xmlOutput.unsetConvention() + + def sbomOutputLocation = findProperty('sbomOutputLocation') + it.jsonOutput.set(sbomOutputLocation.get()) + it.outputs.file(sbomOutputLocation) + + // cyclonedx does not support "choosing" the license placed in the sbom + // see: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/16 + it.doLast { + def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0'] + + def pickLicense = { List licenses -> + if (!(licenses instanceof List) || licenses.isEmpty()) { + return null + } + + def licenseIds = licenses.findAll { it instanceof Map && it.license instanceof Map && it.license.id } + def foundLicense = preferredLicenses.find { p -> licenseIds.any { it.license.id == p } } + if (foundLicense) { + return licenseIds.find { it.license.id == foundLicense } + } + + licenses[0] // pick the first one found + } + + def rewriteSbom = { File f -> + def bom = new JsonSlurper().parse(f) + + // timestamp is not reproducible: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/292 + bom.metadata.timestamp = DateTimeFormatter.ISO_INSTANT.format(buildDate.truncatedTo(ChronoUnit.SECONDS)) + + // components[*].licenses + def comps = (bom instanceof Map && bom.components instanceof List) ? bom.components : [] + comps.each { c -> + if (c instanceof Map && c.licenses instanceof List && !c.licenses.isEmpty()) { + def chosen = pickLicense(c.licenses as List) + if (chosen != null) { + c.licenses = [chosen] + } + } + } + + // force the serialNumber to be reproducible by removing it & recalculating + bom.serialNumber = '' + String withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom)) + def uuid = UUID.nameUUIDFromBytes(withOutSerial.getBytes(StandardCharsets.UTF_8.name())) + def urn = "urn:uuid:${uuid.toString()}" as String + bom.serialNumber = urn + + f.setText(JsonOutput.prettyPrint(JsonOutput.toJson(bom)), StandardCharsets.UTF_8.name()) + + logger.info('Rewrote JSON SBOM ({}) to pick preferred license', project.relativePath(f)) + } + + sbomOutputLocation.get().with { rewriteSbom(it.asFile) } + } +} + +// for now, we only publish the sbom inside of the binary jar (our bom projects won't have a jar file) +pluginManager.withPlugin('java') { + if (!project.findProperty('skipJavaComponent')) { + tasks.named('jar', Jar).configure { Jar jar -> + jar.dependsOn tasks.named('cyclonedxBom') + + jar.from(findProperty('sbomOutputLocation')) { + into('META-INF') + rename { + 'sbom.json' + } + } + } + } +} diff --git a/grails-forge/grails-cli/build.gradle b/grails-forge/grails-cli/build.gradle index 59e79ba704d..7a31e842ba8 100644 --- a/grails-forge/grails-cli/build.gradle +++ b/grails-forge/grails-cli/build.gradle @@ -79,6 +79,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } // It's surprisingly hard to generate start scripts and *not* have them nested into a bin / lib directory diff --git a/grails-forge/grails-forge-cli/build.gradle b/grails-forge/grails-forge-cli/build.gradle index 2422caec5a3..27cd8fb0370 100644 --- a/grails-forge/grails-forge-cli/build.gradle +++ b/grails-forge/grails-forge-cli/build.gradle @@ -111,6 +111,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/doc-config.gradle') } diff --git a/grails-forge/grails-forge-core/build.gradle b/grails-forge/grails-forge-core/build.gradle index ef008f864d3..ca99b008ab0 100644 --- a/grails-forge/grails-forge-core/build.gradle +++ b/grails-forge/grails-forge-core/build.gradle @@ -117,6 +117,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/doc-config.gradle') } diff --git a/grails-geb/build.gradle b/grails-geb/build.gradle index 52428c07ddd..857eb49b2c7 100644 --- a/grails-geb/build.gradle +++ b/grails-geb/build.gradle @@ -68,6 +68,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } \ No newline at end of file diff --git a/grails-gradle/bom/build.gradle b/grails-gradle/bom/build.gradle index 6dfe37f740c..64c087c1fec 100644 --- a/grails-gradle/bom/build.gradle +++ b/grails-gradle/bom/build.gradle @@ -165,4 +165,5 @@ ext { apply { from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } diff --git a/grails-gradle/buildSrc/build.gradle b/grails-gradle/buildSrc/build.gradle index 6595dd9274f..5aabb668410 100644 --- a/grails-gradle/buildSrc/build.gradle +++ b/grails-gradle/buildSrc/build.gradle @@ -64,4 +64,5 @@ file('../../gradle.properties').withInputStream { dependencies { implementation "${gradleBomDependencies['grails-publish-plugin']}" implementation "org.gradle.crypto.checksum:org.gradle.crypto.checksum.gradle.plugin:${gradleProperties.gradleChecksumPluginVersion}" + implementation "org.cyclonedx.bom:org.cyclonedx.bom.gradle.plugin:${gradleProperties.gradleCycloneDxPluginVersion}" } \ No newline at end of file diff --git a/grails-gradle/common/build.gradle b/grails-gradle/common/build.gradle index b6cb6281e4e..f1c840bfa16 100644 --- a/grails-gradle/common/build.gradle +++ b/grails-gradle/common/build.gradle @@ -54,4 +54,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-gradle/docs-core/build.gradle b/grails-gradle/docs-core/build.gradle index 92d81c72abd..b98045ea343 100644 --- a/grails-gradle/docs-core/build.gradle +++ b/grails-gradle/docs-core/build.gradle @@ -33,6 +33,7 @@ configurations { ext { pomDescription = 'Support for Grails documentation generation in Gradle builds.' + sbomProjectType = org.cyclonedx.model.Component.Type.LIBRARY.name() } dependencies { @@ -91,4 +92,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-gradle/gradle/sbom-config.gradle b/grails-gradle/gradle/sbom-config.gradle new file mode 100644 index 00000000000..bab4a25f055 --- /dev/null +++ b/grails-gradle/gradle/sbom-config.gradle @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +import org.cyclonedx.gradle.CycloneDxTask +import org.cyclonedx.model.ExternalReference +import org.cyclonedx.model.LicenseChoice +import org.cyclonedx.model.License +import org.cyclonedx.model.OrganizationalContact +import org.cyclonedx.model.OrganizationalEntity +import org.cyclonedx.model.Component + +import java.nio.charset.StandardCharsets +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit + +apply plugin: 'org.cyclonedx.bom' + +project.ext.setProperty('sbomOutputLocation', project.layout.buildDirectory.file("${findProperty('pomArtifactId') ?: project.name}-${projectVersion}-sbom.json")) + +def sbomTask = tasks.named('cyclonedxBom', CycloneDxTask) +sbomTask.configure { CycloneDxTask it -> + // the 2.x version of Cyclonedx uses a legacy syntax & helpers for setting inputs so the syntax below + // is required until the 3.x version is GA + it.setProjectType(findProperty('sbomProjectType') ?: Component.Type.FRAMEWORK.name()) + it.@componentName.set(findProperty('pomArtifactId') ?: project.name) + it.@organizationalEntity.set(new OrganizationalEntity().tap { + name = 'Apache Software Foundation' + urls = ['https://www.apache.org/', 'https://security.apache.org/'] + addContact(new OrganizationalContact().tap { + name = 'Apache Grails Development Team' + email = 'dev@grails.apache.org' + }) + }) + it.@licenseChoice.set(new LicenseChoice().tap { + addLicense(new License().tap { + name = 'Apache-2.0' + url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' + }) + }) + + it.@externalReferences.set([ + new ExternalReference().tap { + url = 'https://grails.apache.org/' + type = ExternalReference.Type.WEBSITE + } + ]) + + // sboms are published for the purposes of vulnerability analysis so only include the runtime classpath + it.@includeConfigs.set(['runtimeClasspath']) + it.@skipConfigs.set(['compileClasspath', 'testRuntimeClasspath']) + + // disable xml output + it.xmlOutput.unsetConvention() + + def sbomOutputLocation = findProperty('sbomOutputLocation') + it.jsonOutput.set(sbomOutputLocation.get()) + it.outputs.file(sbomOutputLocation) + + // cyclonedx does not support "choosing" the license placed in the sbom + // see: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/16 + it.doLast { + def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0'] + + def pickLicense = { List licenses -> + if (!(licenses instanceof List) || licenses.isEmpty()) { + return null + } + + def licenseIds = licenses.findAll { it instanceof Map && it.license instanceof Map && it.license.id } + def foundLicense = preferredLicenses.find { p -> licenseIds.any { it.license.id == p } } + if (foundLicense) { + return licenseIds.find { it.license.id == foundLicense } + } + + licenses[0] // pick the first one found + } + + def rewriteSbom = { File f -> + def bom = new JsonSlurper().parse(f) + + // timestamp is not reproducible: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/292 + bom.metadata.timestamp = DateTimeFormatter.ISO_INSTANT.format(buildDate.truncatedTo(ChronoUnit.SECONDS)) + + // components[*].licenses + def comps = (bom instanceof Map && bom.components instanceof List) ? bom.components : [] + comps.each { c -> + if (c instanceof Map && c.licenses instanceof List && !c.licenses.isEmpty()) { + def chosen = pickLicense(c.licenses as List) + if (chosen != null) { + c.licenses = [chosen] + } + } + } + + // force the serialNumber to be reproducible by removing it & recalculating + bom.serialNumber = '' + String withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom)) + def uuid = UUID.nameUUIDFromBytes(withOutSerial.getBytes(StandardCharsets.UTF_8.name())) + def urn = "urn:uuid:${uuid.toString()}" as String + bom.serialNumber = urn + + f.setText(JsonOutput.prettyPrint(JsonOutput.toJson(bom)), StandardCharsets.UTF_8.name()) + + logger.info('Rewrote JSON SBOM ({}) to pick preferred license', project.relativePath(f)) + } + + sbomOutputLocation.get().with { rewriteSbom(it.asFile) } + } +} + +// for now, we only publish the sbom inside of the binary jar (our bom projects won't have a jar file nor will starters that just collect dependencies) +pluginManager.withPlugin('java') { + if (!project.findProperty('skipJavaComponent')) { + tasks.named('jar', Jar).configure { Jar jar -> + jar.dependsOn tasks.named('cyclonedxBom') + + jar.from(findProperty('sbomOutputLocation')) { + into('META-INF') + rename { + 'sbom.json' + } + } + } + } +} diff --git a/grails-gradle/model/build.gradle b/grails-gradle/model/build.gradle index 5ee6d02e96f..08c5fa86c6b 100644 --- a/grails-gradle/model/build.gradle +++ b/grails-gradle/model/build.gradle @@ -31,6 +31,7 @@ ext { pomTitle = 'Grails Gradle Model' pomDescription = 'Classes to support the Grails Gradle Plugins' pomDevelopers = [graemerocher: 'Graeme Rocher'] + sbomProjectType = org.cyclonedx.model.Component.Type.LIBRARY.name() } dependencies { @@ -80,4 +81,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-gradle/plugins/build.gradle b/grails-gradle/plugins/build.gradle index 01b230951e2..68b8cda97bb 100644 --- a/grails-gradle/plugins/build.gradle +++ b/grails-gradle/plugins/build.gradle @@ -124,4 +124,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } diff --git a/grails-gradle/tasks/build.gradle b/grails-gradle/tasks/build.gradle index 8e20aeb902b..e72a1d82e69 100644 --- a/grails-gradle/tasks/build.gradle +++ b/grails-gradle/tasks/build.gradle @@ -29,6 +29,7 @@ group = 'org.apache.grails' ext { pomTitle = 'Grails Gradle Tasks' pomDescription = 'Various bundled Gradle tasks for Grails projects' + sbomProjectType = org.cyclonedx.model.Component.Type.LIBRARY.name() } dependencies { @@ -52,4 +53,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-gsp/core/build.gradle b/grails-gsp/core/build.gradle index 07b4d5a5f15..b9acca589b7 100644 --- a/grails-gsp/core/build.gradle +++ b/grails-gsp/core/build.gradle @@ -93,5 +93,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-gsp/grails-layout/build.gradle b/grails-gsp/grails-layout/build.gradle index 7a1403ac627..389897eefb4 100644 --- a/grails-gsp/grails-layout/build.gradle +++ b/grails-gsp/grails-layout/build.gradle @@ -64,6 +64,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } \ No newline at end of file diff --git a/grails-gsp/grails-sitemesh3/build.gradle b/grails-gsp/grails-sitemesh3/build.gradle index 5453a52427f..edbe66e4fb9 100644 --- a/grails-gsp/grails-sitemesh3/build.gradle +++ b/grails-gsp/grails-sitemesh3/build.gradle @@ -85,6 +85,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } \ No newline at end of file diff --git a/grails-gsp/grails-taglib/build.gradle b/grails-gsp/grails-taglib/build.gradle index ca0d47a9aa5..09f4fe203c5 100644 --- a/grails-gsp/grails-taglib/build.gradle +++ b/grails-gsp/grails-taglib/build.gradle @@ -84,5 +84,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } diff --git a/grails-gsp/grails-web-gsp-taglib/build.gradle b/grails-gsp/grails-web-gsp-taglib/build.gradle index a566d7346bf..5e4b6d7ae45 100644 --- a/grails-gsp/grails-web-gsp-taglib/build.gradle +++ b/grails-gsp/grails-web-gsp-taglib/build.gradle @@ -63,5 +63,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } diff --git a/grails-gsp/grails-web-gsp/build.gradle b/grails-gsp/grails-web-gsp/build.gradle index 5fdf1575030..dd1050a901a 100644 --- a/grails-gsp/grails-web-gsp/build.gradle +++ b/grails-gsp/grails-web-gsp/build.gradle @@ -131,5 +131,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } diff --git a/grails-gsp/grails-web-jsp/build.gradle b/grails-gsp/grails-web-jsp/build.gradle index d2ac2fcedfc..c4420a7db09 100644 --- a/grails-gsp/grails-web-jsp/build.gradle +++ b/grails-gsp/grails-web-jsp/build.gradle @@ -112,5 +112,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } diff --git a/grails-gsp/grails-web-taglib/build.gradle b/grails-gsp/grails-web-taglib/build.gradle index 3e646f238ae..ba730c2ac3b 100644 --- a/grails-gsp/grails-web-taglib/build.gradle +++ b/grails-gsp/grails-web-taglib/build.gradle @@ -116,5 +116,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-gsp/plugin/build.gradle b/grails-gsp/plugin/build.gradle index 5b2320d89ed..8903ee9d0a6 100644 --- a/grails-gsp/plugin/build.gradle +++ b/grails-gsp/plugin/build.gradle @@ -184,6 +184,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } diff --git a/grails-i18n/build.gradle b/grails-i18n/build.gradle index 5575f1bcc6e..707abd5fd04 100644 --- a/grails-i18n/build.gradle +++ b/grails-i18n/build.gradle @@ -67,4 +67,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-interceptors/build.gradle b/grails-interceptors/build.gradle index 178cc862cbc..023bf496ff7 100644 --- a/grails-interceptors/build.gradle +++ b/grails-interceptors/build.gradle @@ -66,5 +66,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-logging/build.gradle b/grails-logging/build.gradle index beef4f22685..d0c256a0078 100644 --- a/grails-logging/build.gradle +++ b/grails-logging/build.gradle @@ -57,5 +57,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-micronaut/build.gradle b/grails-micronaut/build.gradle index 8648a527710..21cfa03d6fc 100644 --- a/grails-micronaut/build.gradle +++ b/grails-micronaut/build.gradle @@ -45,4 +45,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-mimetypes/build.gradle b/grails-mimetypes/build.gradle index 4fb2f63bec4..ddac43902aa 100644 --- a/grails-mimetypes/build.gradle +++ b/grails-mimetypes/build.gradle @@ -65,5 +65,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-profiles/base/build.gradle b/grails-profiles/base/build.gradle index 305150a2bb1..c8139b9999d 100644 --- a/grails-profiles/base/build.gradle +++ b/grails-profiles/base/build.gradle @@ -60,4 +60,5 @@ tasks.named('compileProfile').configure { apply { from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-profiles/plugin/build.gradle b/grails-profiles/plugin/build.gradle index c20f6499d31..a52fd75f3fe 100644 --- a/grails-profiles/plugin/build.gradle +++ b/grails-profiles/plugin/build.gradle @@ -34,4 +34,5 @@ dependencies { apply { from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-profiles/profile/build.gradle b/grails-profiles/profile/build.gradle index 7c62b1c9c46..30d5c394e7a 100644 --- a/grails-profiles/profile/build.gradle +++ b/grails-profiles/profile/build.gradle @@ -54,4 +54,5 @@ tasks.named('compileProfile').configure { apply { from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-profiles/rest-api-plugin/build.gradle b/grails-profiles/rest-api-plugin/build.gradle index 136581327e0..42979ed9fb3 100644 --- a/grails-profiles/rest-api-plugin/build.gradle +++ b/grails-profiles/rest-api-plugin/build.gradle @@ -35,4 +35,5 @@ dependencies { apply { from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-profiles/rest-api/build.gradle b/grails-profiles/rest-api/build.gradle index c257beb5be1..ea994484a81 100644 --- a/grails-profiles/rest-api/build.gradle +++ b/grails-profiles/rest-api/build.gradle @@ -34,4 +34,5 @@ dependencies { apply { from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-profiles/web-plugin/build.gradle b/grails-profiles/web-plugin/build.gradle index ffed8372fe7..614567b345d 100644 --- a/grails-profiles/web-plugin/build.gradle +++ b/grails-profiles/web-plugin/build.gradle @@ -35,4 +35,5 @@ dependencies { apply { from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } diff --git a/grails-profiles/web/build.gradle b/grails-profiles/web/build.gradle index cf2d6c335ea..b73180727e5 100644 --- a/grails-profiles/web/build.gradle +++ b/grails-profiles/web/build.gradle @@ -34,4 +34,5 @@ dependencies { apply { from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } diff --git a/grails-rest-transforms/build.gradle b/grails-rest-transforms/build.gradle index 40e93a75632..be8345be3d8 100644 --- a/grails-rest-transforms/build.gradle +++ b/grails-rest-transforms/build.gradle @@ -77,5 +77,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-scaffolding/build.gradle b/grails-scaffolding/build.gradle index c21a4e5c044..bc0b3f118b8 100644 --- a/grails-scaffolding/build.gradle +++ b/grails-scaffolding/build.gradle @@ -47,6 +47,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } \ No newline at end of file diff --git a/grails-services/build.gradle b/grails-services/build.gradle index b4da45e80d8..dc94db6afd8 100644 --- a/grails-services/build.gradle +++ b/grails-services/build.gradle @@ -68,4 +68,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-shell-cli/build.gradle b/grails-shell-cli/build.gradle index ee789751961..cd70c43d950 100644 --- a/grails-shell-cli/build.gradle +++ b/grails-shell-cli/build.gradle @@ -127,6 +127,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } diff --git a/grails-spring/build.gradle b/grails-spring/build.gradle index ef54cccd23e..9395164cb6d 100644 --- a/grails-spring/build.gradle +++ b/grails-spring/build.gradle @@ -61,4 +61,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-test-core/build.gradle b/grails-test-core/build.gradle index d6985226174..b619710528a 100644 --- a/grails-test-core/build.gradle +++ b/grails-test-core/build.gradle @@ -88,4 +88,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-testing-support-core/build.gradle b/grails-testing-support-core/build.gradle index 4a816f0f60e..e509bb27388 100644 --- a/grails-testing-support-core/build.gradle +++ b/grails-testing-support-core/build.gradle @@ -78,5 +78,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-testing-support-datamapping/build.gradle b/grails-testing-support-datamapping/build.gradle index eed573a2866..9ab8606bc9f 100755 --- a/grails-testing-support-datamapping/build.gradle +++ b/grails-testing-support-datamapping/build.gradle @@ -139,5 +139,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-testing-support-mongodb/build.gradle b/grails-testing-support-mongodb/build.gradle index 7f480cebb22..938c1867f1b 100644 --- a/grails-testing-support-mongodb/build.gradle +++ b/grails-testing-support-mongodb/build.gradle @@ -47,5 +47,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } \ No newline at end of file diff --git a/grails-testing-support-views-gson/build.gradle b/grails-testing-support-views-gson/build.gradle index b8f42edf2e0..3de827ad7f0 100644 --- a/grails-testing-support-views-gson/build.gradle +++ b/grails-testing-support-views-gson/build.gradle @@ -53,5 +53,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') } diff --git a/grails-testing-support-web/build.gradle b/grails-testing-support-web/build.gradle index e5f8bc99381..299bee05cd6 100755 --- a/grails-testing-support-web/build.gradle +++ b/grails-testing-support-web/build.gradle @@ -53,5 +53,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-url-mappings/build.gradle b/grails-url-mappings/build.gradle index 2d917cee1f8..ef6482702e3 100644 --- a/grails-url-mappings/build.gradle +++ b/grails-url-mappings/build.gradle @@ -68,4 +68,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-validation/build.gradle b/grails-validation/build.gradle index 26e5bb34347..74d9d7f4a40 100644 --- a/grails-validation/build.gradle +++ b/grails-validation/build.gradle @@ -66,5 +66,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-views-core/build.gradle b/grails-views-core/build.gradle index a54475a5f9b..d9a310b17af 100644 --- a/grails-views-core/build.gradle +++ b/grails-views-core/build.gradle @@ -56,5 +56,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-views-gson/build.gradle b/grails-views-gson/build.gradle index c7fbbdcbe0c..6b271697b46 100644 --- a/grails-views-gson/build.gradle +++ b/grails-views-gson/build.gradle @@ -68,6 +68,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } \ No newline at end of file diff --git a/grails-views-markup/build.gradle b/grails-views-markup/build.gradle index 4530280e2e6..910af14a1f4 100644 --- a/grails-views-markup/build.gradle +++ b/grails-views-markup/build.gradle @@ -57,6 +57,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') } \ No newline at end of file diff --git a/grails-web-boot/build.gradle b/grails-web-boot/build.gradle index f1e2455cd56..5d387223f88 100644 --- a/grails-web-boot/build.gradle +++ b/grails-web-boot/build.gradle @@ -70,5 +70,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-web-common/build.gradle b/grails-web-common/build.gradle index 189b9677a6a..7eb8bce67d6 100644 --- a/grails-web-common/build.gradle +++ b/grails-web-common/build.gradle @@ -81,5 +81,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-web-core/build.gradle b/grails-web-core/build.gradle index a434004036d..867bc32f79d 100644 --- a/grails-web-core/build.gradle +++ b/grails-web-core/build.gradle @@ -75,5 +75,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-web-databinding/build.gradle b/grails-web-databinding/build.gradle index 73f47df1395..8fda4ed286b 100644 --- a/grails-web-databinding/build.gradle +++ b/grails-web-databinding/build.gradle @@ -74,5 +74,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-web-mvc/build.gradle b/grails-web-mvc/build.gradle index 0f4923c7649..e4b08ea68b7 100644 --- a/grails-web-mvc/build.gradle +++ b/grails-web-mvc/build.gradle @@ -66,5 +66,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-web-url-mappings/build.gradle b/grails-web-url-mappings/build.gradle index 7e67bdb7489..b97d7a0c0ee 100644 --- a/grails-web-url-mappings/build.gradle +++ b/grails-web-url-mappings/build.gradle @@ -79,5 +79,6 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') } \ No newline at end of file diff --git a/grails-wrapper/build.gradle b/grails-wrapper/build.gradle index 82e67d17271..8d70f1b80b4 100644 --- a/grails-wrapper/build.gradle +++ b/grails-wrapper/build.gradle @@ -56,6 +56,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') } // It's surprisingly hard to generate start scripts and *not* have them nested into a bin / lib directory From b9a62fafdd6598f5e3dd0a06500fadd2c2d53958 Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Fri, 19 Sep 2025 09:15:06 -0400 Subject: [PATCH 03/10] fix: remove the license report plugin since it's no longer maintained (https://github.com/hierynomus/license-gradle-plugin/issues/219) --- build.gradle | 2 -- buildSrc/build.gradle | 4 --- gradle.properties | 1 - gradle/dependency-licenses.gradle | 30 ------------------- grails-forge/build.gradle | 2 -- grails-forge/buildSrc/build.gradle | 4 --- .../gradle/dependency-licenses.gradle | 30 ------------------- 7 files changed, 73 deletions(-) delete mode 100644 gradle/dependency-licenses.gradle delete mode 100644 grails-forge/gradle/dependency-licenses.gradle diff --git a/build.gradle b/build.gradle index 16e0df381af..4a96394b39d 100644 --- a/build.gradle +++ b/build.gradle @@ -105,8 +105,6 @@ subprojects { cacheChangingModulesFor(cacheHours, 'hours') } } - - apply from: rootProject.layout.projectDirectory.file('gradle/dependency-licenses.gradle') } apply { diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 0e5cd7f88f2..97ef6d02863 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -63,10 +63,6 @@ repositories { dependencies { implementation platform("org.apache.grails:grails-gradle-bom:${gradleProperties.projectVersion}") implementation 'org.apache.grails.gradle:grails-publish' - implementation "gradle.plugin.com.hierynomus.gradle.plugins:license-gradle-plugin:${gradleProperties.gradleLicensePluginVersion}", { - // Due to https://github.com/hierynomus/license-gradle-plugin/issues/161, spring must be excluded - exclude group: 'org.springframework', module: 'spring-core' - } implementation 'cloud.wondrify:asset-pipeline-gradle' implementation 'org.apache.grails:grails-docs-core' implementation 'org.apache.grails:grails-gradle-plugins' diff --git a/gradle.properties b/gradle.properties index 83805e20bbc..31b231d02c4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -49,7 +49,6 @@ apacheRatVersion=0.8.1 gradleChecksumPluginVersion=1.4.0 # note: the cyclonedx 3.0.0-alpha-1 still does not set the project correctly, so we must use the older version gradleCycloneDxPluginVersion=2.4.0 -gradleLicensePluginVersion=0.16.1 # micronaut libraries not in the bom due to the potential for spring mismatches micronautPlatformVersion=4.9.2 diff --git a/gradle/dependency-licenses.gradle b/gradle/dependency-licenses.gradle deleted file mode 100644 index 24c7f836969..00000000000 --- a/gradle/dependency-licenses.gradle +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -apply plugin: 'com.github.hierynomus.license-report' - -List licenseExclusions = rootProject.subprojects.collect { - "${it.group}:${it.findProperty('pomArtifactId') ?: it.name}:${rootProject.projectVersion}" as String -} - -downloadLicenses { - includeProjectDependencies = true - dependencyConfiguration = 'runtimeClasspath' - excludeDependencies = licenseExclusions -} \ No newline at end of file diff --git a/grails-forge/build.gradle b/grails-forge/build.gradle index 9fc3bfb5f21..571584a9859 100644 --- a/grails-forge/build.gradle +++ b/grails-forge/build.gradle @@ -99,8 +99,6 @@ subprojects { cacheChangingModulesFor(cacheHours, 'hours') } } - - apply from: rootProject.layout.projectDirectory.file('gradle/dependency-licenses.gradle') } apply { diff --git a/grails-forge/buildSrc/build.gradle b/grails-forge/buildSrc/build.gradle index 70b37a785d1..7ad7cf0435a 100644 --- a/grails-forge/buildSrc/build.gradle +++ b/grails-forge/buildSrc/build.gradle @@ -77,10 +77,6 @@ configurations.configureEach { dependencies { implementation "org.apache.grails.gradle:grails-publish:$grailsPublishGradleVersion" - implementation "gradle.plugin.com.hierynomus.gradle.plugins:license-gradle-plugin:${rootProperties.gradleLicensePluginVersion}", { - // Due to https://github.com/hierynomus/license-gradle-plugin/issues/161, spring must be excluded - exclude group: 'org.springframework', module: 'spring-core' - } implementation "io.micronaut.build.internal:micronaut-gradle-plugins:$micronautGradlePlugins" implementation "com.fizzed:rocker-compiler:$rockerVersion" implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion" diff --git a/grails-forge/gradle/dependency-licenses.gradle b/grails-forge/gradle/dependency-licenses.gradle deleted file mode 100644 index 24c7f836969..00000000000 --- a/grails-forge/gradle/dependency-licenses.gradle +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -apply plugin: 'com.github.hierynomus.license-report' - -List licenseExclusions = rootProject.subprojects.collect { - "${it.group}:${it.findProperty('pomArtifactId') ?: it.name}:${rootProject.projectVersion}" as String -} - -downloadLicenses { - includeProjectDependencies = true - dependencyConfiguration = 'runtimeClasspath' - excludeDependencies = licenseExclusions -} \ No newline at end of file From 8923862c04b935d20ab5157387b418974407f189 Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Fri, 19 Sep 2025 13:14:37 -0400 Subject: [PATCH 04/10] feature: strictly validate licenses pulled in by dependencies --- gradle/sbom-config.gradle | 138 ++++++++++++++++++- grails-forge/gradle/publish-config.gradle | 1 - grails-forge/gradle/sbom-config.gradle | 143 -------------------- grails-forge/grails-cli/build.gradle | 2 +- grails-forge/grails-forge-cli/build.gradle | 2 +- grails-forge/grails-forge-core/build.gradle | 2 +- grails-gradle/bom/build.gradle | 2 +- grails-gradle/common/build.gradle | 2 +- grails-gradle/docs-core/build.gradle | 2 +- grails-gradle/gradle/sbom-config.gradle | 143 -------------------- grails-gradle/model/build.gradle | 2 +- grails-gradle/plugins/build.gradle | 2 +- grails-gradle/tasks/build.gradle | 2 +- 13 files changed, 140 insertions(+), 303 deletions(-) delete mode 100644 grails-forge/gradle/sbom-config.gradle delete mode 100644 grails-gradle/gradle/sbom-config.gradle diff --git a/gradle/sbom-config.gradle b/gradle/sbom-config.gradle index 779e6ac1f34..ad0b516c0b7 100644 --- a/gradle/sbom-config.gradle +++ b/gradle/sbom-config.gradle @@ -77,22 +77,146 @@ sbomTask.configure { CycloneDxTask it -> // cyclonedx does not support "choosing" the license placed in the sbom // see: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/16 it.doLast { - def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0'] + // ordered so that first value is the most preferred + def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0', 'UPL-1.0'] - def pickLicense = { List licenses -> - if (!(licenses instanceof List) || licenses.isEmpty()) { - return null + // licenses are standardized @ https://spdx.org/licenses/ + def licenses = [ + 'Apache-2.0' : [ + id: 'Apache-2.0', + name: 'Apache License 2.0', + text: [ + contentType: 'text/plain', + encoding: 'base64', + content: 'QXBhY2hlIExpY2Vuc2UKVmVyc2lvbiAyLjAsIEphbnVhcnkgMjAwNApodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvCgpURVJNUyBBTkQgQ09ORElUSU9OUyBGT1IgVVNFLCBSRVBST0RVQ1RJT04sIEFORCBESVNUUklCVVRJT04KCjEuIERlZmluaXRpb25zLgoKIkxpY2Vuc2UiIHNoYWxsIG1lYW4gdGhlIHRlcm1zIGFuZCBjb25kaXRpb25zIGZvciB1c2UsIHJlcHJvZHVjdGlvbiwgYW5kIGRpc3RyaWJ1dGlvbiBhcyBkZWZpbmVkIGJ5IFNlY3Rpb25zIDEgdGhyb3VnaCA5IG9mIHRoaXMgZG9jdW1lbnQuCgoiTGljZW5zb3IiIHNoYWxsIG1lYW4gdGhlIGNvcHlyaWdodCBvd25lciBvciBlbnRpdHkgYXV0aG9yaXplZCBieSB0aGUgY29weXJpZ2h0IG93bmVyIHRoYXQgaXMgZ3JhbnRpbmcgdGhlIExpY2Vuc2UuCgoiTGVnYWwgRW50aXR5IiBzaGFsbCBtZWFuIHRoZSB1bmlvbiBvZiB0aGUgYWN0aW5nIGVudGl0eSBhbmQgYWxsIG90aGVyIGVudGl0aWVzIHRoYXQgY29udHJvbCwgYXJlIGNvbnRyb2xsZWQgYnksIG9yIGFyZSB1bmRlciBjb21tb24gY29udHJvbCB3aXRoIHRoYXQgZW50aXR5LiBGb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgZGVmaW5pdGlvbiwgImNvbnRyb2wiIG1lYW5zIChpKSB0aGUgcG93ZXIsIGRpcmVjdCBvciBpbmRpcmVjdCwgdG8gY2F1c2UgdGhlIGRpcmVjdGlvbiBvciBtYW5hZ2VtZW50IG9mIHN1Y2ggZW50aXR5LCB3aGV0aGVyIGJ5IGNvbnRyYWN0IG9yIG90aGVyd2lzZSwgb3IgKGlpKSBvd25lcnNoaXAgb2YgZmlmdHkgcGVyY2VudCAoNTAlKSBvciBtb3JlIG9mIHRoZSBvdXRzdGFuZGluZyBzaGFyZXMsIG9yIChpaWkpIGJlbmVmaWNpYWwgb3duZXJzaGlwIG9mIHN1Y2ggZW50aXR5LgoKIllvdSIgKG9yICJZb3VyIikgc2hhbGwgbWVhbiBhbiBpbmRpdmlkdWFsIG9yIExlZ2FsIEVudGl0eSBleGVyY2lzaW5nIHBlcm1pc3Npb25zIGdyYW50ZWQgYnkgdGhpcyBMaWNlbnNlLgoKIlNvdXJjZSIgZm9ybSBzaGFsbCBtZWFuIHRoZSBwcmVmZXJyZWQgZm9ybSBmb3IgbWFraW5nIG1vZGlmaWNhdGlvbnMsIGluY2x1ZGluZyBidXQgbm90IGxpbWl0ZWQgdG8gc29mdHdhcmUgc291cmNlIGNvZGUsIGRvY3VtZW50YXRpb24gc291cmNlLCBhbmQgY29uZmlndXJhdGlvbiBmaWxlcy4KCiJPYmplY3QiIGZvcm0gc2hhbGwgbWVhbiBhbnkgZm9ybSByZXN1bHRpbmcgZnJvbSBtZWNoYW5pY2FsIHRyYW5zZm9ybWF0aW9uIG9yIHRyYW5zbGF0aW9uIG9mIGEgU291cmNlIGZvcm0sIGluY2x1ZGluZyBidXQgbm90IGxpbWl0ZWQgdG8gY29tcGlsZWQgb2JqZWN0IGNvZGUsIGdlbmVyYXRlZCBkb2N1bWVudGF0aW9uLCBhbmQgY29udmVyc2lvbnMgdG8gb3RoZXIgbWVkaWEgdHlwZXMuCgoiV29yayIgc2hhbGwgbWVhbiB0aGUgd29yayBvZiBhdXRob3JzaGlwLCB3aGV0aGVyIGluIFNvdXJjZSBvciBPYmplY3QgZm9ybSwgbWFkZSBhdmFpbGFibGUgdW5kZXIgdGhlIExpY2Vuc2UsIGFzIGluZGljYXRlZCBieSBhIGNvcHlyaWdodCBub3RpY2UgdGhhdCBpcyBpbmNsdWRlZCBpbiBvciBhdHRhY2hlZCB0byB0aGUgd29yayAoYW4gZXhhbXBsZSBpcyBwcm92aWRlZCBpbiB0aGUgQXBwZW5kaXggYmVsb3cpLgoKIkRlcml2YXRpdmUgV29ya3MiIHNoYWxsIG1lYW4gYW55IHdvcmssIHdoZXRoZXIgaW4gU291cmNlIG9yIE9iamVjdCBmb3JtLCB0aGF0IGlzIGJhc2VkIG9uIChvciBkZXJpdmVkIGZyb20pIHRoZSBXb3JrIGFuZCBmb3Igd2hpY2ggdGhlIGVkaXRvcmlhbCByZXZpc2lvbnMsIGFubm90YXRpb25zLCBlbGFib3JhdGlvbnMsIG9yIG90aGVyIG1vZGlmaWNhdGlvbnMgcmVwcmVzZW50LCBhcyBhIHdob2xlLCBhbiBvcmlnaW5hbCB3b3JrIG9mIGF1dGhvcnNoaXAuIEZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyBMaWNlbnNlLCBEZXJpdmF0aXZlIFdvcmtzIHNoYWxsIG5vdCBpbmNsdWRlIHdvcmtzIHRoYXQgcmVtYWluIHNlcGFyYWJsZSBmcm9tLCBvciBtZXJlbHkgbGluayAob3IgYmluZCBieSBuYW1lKSB0byB0aGUgaW50ZXJmYWNlcyBvZiwgdGhlIFdvcmsgYW5kIERlcml2YXRpdmUgV29ya3MgdGhlcmVvZi4KCiJDb250cmlidXRpb24iIHNoYWxsIG1lYW4gYW55IHdvcmsgb2YgYXV0aG9yc2hpcCwgaW5jbHVkaW5nIHRoZSBvcmlnaW5hbCB2ZXJzaW9uIG9mIHRoZSBXb3JrIGFuZCBhbnkgbW9kaWZpY2F0aW9ucyBvciBhZGRpdGlvbnMgdG8gdGhhdCBXb3JrIG9yIERlcml2YXRpdmUgV29ya3MgdGhlcmVvZiwgdGhhdCBpcyBpbnRlbnRpb25hbGx5IHN1Ym1pdHRlZCB0byBMaWNlbnNvciBmb3IgaW5jbHVzaW9uIGluIHRoZSBXb3JrIGJ5IHRoZSBjb3B5cmlnaHQgb3duZXIgb3IgYnkgYW4gaW5kaXZpZHVhbCBvciBMZWdhbCBFbnRpdHkgYXV0aG9yaXplZCB0byBzdWJtaXQgb24gYmVoYWxmIG9mIHRoZSBjb3B5cmlnaHQgb3duZXIuIEZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyBkZWZpbml0aW9uLCAic3VibWl0dGVkIiBtZWFucyBhbnkgZm9ybSBvZiBlbGVjdHJvbmljLCB2ZXJiYWwsIG9yIHdyaXR0ZW4gY29tbXVuaWNhdGlvbiBzZW50IHRvIHRoZSBMaWNlbnNvciBvciBpdHMgcmVwcmVzZW50YXRpdmVzLCBpbmNsdWRpbmcgYnV0IG5vdCBsaW1pdGVkIHRvIGNvbW11bmljYXRpb24gb24gZWxlY3Ryb25pYyBtYWlsaW5nIGxpc3RzLCBzb3VyY2UgY29kZSBjb250cm9sIHN5c3RlbXMsIGFuZCBpc3N1ZSB0cmFja2luZyBzeXN0ZW1zIHRoYXQgYXJlIG1hbmFnZWQgYnksIG9yIG9uIGJlaGFsZiBvZiwgdGhlIExpY2Vuc29yIGZvciB0aGUgcHVycG9zZSBvZiBkaXNjdXNzaW5nIGFuZCBpbXByb3ZpbmcgdGhlIFdvcmssIGJ1dCBleGNsdWRpbmcgY29tbXVuaWNhdGlvbiB0aGF0IGlzIGNvbnNwaWN1b3VzbHkgbWFya2VkIG9yIG90aGVyd2lzZSBkZXNpZ25hdGVkIGluIHdyaXRpbmcgYnkgdGhlIGNvcHlyaWdodCBvd25lciBhcyAiTm90IGEgQ29udHJpYnV0aW9uLiIKCiJDb250cmlidXRvciIgc2hhbGwgbWVhbiBMaWNlbnNvciBhbmQgYW55IGluZGl2aWR1YWwgb3IgTGVnYWwgRW50aXR5IG9uIGJlaGFsZiBvZiB3aG9tIGEgQ29udHJpYnV0aW9uIGhhcyBiZWVuIHJlY2VpdmVkIGJ5IExpY2Vuc29yIGFuZCBzdWJzZXF1ZW50bHkgaW5jb3Jwb3JhdGVkIHdpdGhpbiB0aGUgV29yay4KCjIuIEdyYW50IG9mIENvcHlyaWdodCBMaWNlbnNlLiBTdWJqZWN0IHRvIHRoZSB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB0aGlzIExpY2Vuc2UsIGVhY2ggQ29udHJpYnV0b3IgaGVyZWJ5IGdyYW50cyB0byBZb3UgYSBwZXJwZXR1YWwsIHdvcmxkd2lkZSwgbm9uLWV4Y2x1c2l2ZSwgbm8tY2hhcmdlLCByb3lhbHR5LWZyZWUsIGlycmV2b2NhYmxlIGNvcHlyaWdodCBsaWNlbnNlIHRvIHJlcHJvZHVjZSwgcHJlcGFyZSBEZXJpdmF0aXZlIFdvcmtzIG9mLCBwdWJsaWNseSBkaXNwbGF5LCBwdWJsaWNseSBwZXJmb3JtLCBzdWJsaWNlbnNlLCBhbmQgZGlzdHJpYnV0ZSB0aGUgV29yayBhbmQgc3VjaCBEZXJpdmF0aXZlIFdvcmtzIGluIFNvdXJjZSBvciBPYmplY3QgZm9ybS4KCjMuIEdyYW50IG9mIFBhdGVudCBMaWNlbnNlLiBTdWJqZWN0IHRvIHRoZSB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB0aGlzIExpY2Vuc2UsIGVhY2ggQ29udHJpYnV0b3IgaGVyZWJ5IGdyYW50cyB0byBZb3UgYSBwZXJwZXR1YWwsIHdvcmxkd2lkZSwgbm9uLWV4Y2x1c2l2ZSwgbm8tY2hhcmdlLCByb3lhbHR5LWZyZWUsIGlycmV2b2NhYmxlIChleGNlcHQgYXMgc3RhdGVkIGluIHRoaXMgc2VjdGlvbikgcGF0ZW50IGxpY2Vuc2UgdG8gbWFrZSwgaGF2ZSBtYWRlLCB1c2UsIG9mZmVyIHRvIHNlbGwsIHNlbGwsIGltcG9ydCwgYW5kIG90aGVyd2lzZSB0cmFuc2ZlciB0aGUgV29yaywgd2hlcmUgc3VjaCBsaWNlbnNlIGFwcGxpZXMgb25seSB0byB0aG9zZSBwYXRlbnQgY2xhaW1zIGxpY2Vuc2FibGUgYnkgc3VjaCBDb250cmlidXRvciB0aGF0IGFyZSBuZWNlc3NhcmlseSBpbmZyaW5nZWQgYnkgdGhlaXIgQ29udHJpYnV0aW9uKHMpIGFsb25lIG9yIGJ5IGNvbWJpbmF0aW9uIG9mIHRoZWlyIENvbnRyaWJ1dGlvbihzKSB3aXRoIHRoZSBXb3JrIHRvIHdoaWNoIHN1Y2ggQ29udHJpYnV0aW9uKHMpIHdhcyBzdWJtaXR0ZWQuIElmIFlvdSBpbnN0aXR1dGUgcGF0ZW50IGxpdGlnYXRpb24gYWdhaW5zdCBhbnkgZW50aXR5IChpbmNsdWRpbmcgYSBjcm9zcy1jbGFpbSBvciBjb3VudGVyY2xhaW0gaW4gYSBsYXdzdWl0KSBhbGxlZ2luZyB0aGF0IHRoZSBXb3JrIG9yIGEgQ29udHJpYnV0aW9uIGluY29ycG9yYXRlZCB3aXRoaW4gdGhlIFdvcmsgY29uc3RpdHV0ZXMgZGlyZWN0IG9yIGNvbnRyaWJ1dG9yeSBwYXRlbnQgaW5mcmluZ2VtZW50LCB0aGVuIGFueSBwYXRlbnQgbGljZW5zZXMgZ3JhbnRlZCB0byBZb3UgdW5kZXIgdGhpcyBMaWNlbnNlIGZvciB0aGF0IFdvcmsgc2hhbGwgdGVybWluYXRlIGFzIG9mIHRoZSBkYXRlIHN1Y2ggbGl0aWdhdGlvbiBpcyBmaWxlZC4KCjQuIFJlZGlzdHJpYnV0aW9uLiBZb3UgbWF5IHJlcHJvZHVjZSBhbmQgZGlzdHJpYnV0ZSBjb3BpZXMgb2YgdGhlIFdvcmsgb3IgRGVyaXZhdGl2ZSBXb3JrcyB0aGVyZW9mIGluIGFueSBtZWRpdW0sIHdpdGggb3Igd2l0aG91dCBtb2RpZmljYXRpb25zLCBhbmQgaW4gU291cmNlIG9yIE9iamVjdCBmb3JtLCBwcm92aWRlZCB0aGF0IFlvdSBtZWV0IHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoKCiAgICAgKGEpIFlvdSBtdXN0IGdpdmUgYW55IG90aGVyIHJlY2lwaWVudHMgb2YgdGhlIFdvcmsgb3IgRGVyaXZhdGl2ZSBXb3JrcyBhIGNvcHkgb2YgdGhpcyBMaWNlbnNlOyBhbmQKCiAgICAgKGIpIFlvdSBtdXN0IGNhdXNlIGFueSBtb2RpZmllZCBmaWxlcyB0byBjYXJyeSBwcm9taW5lbnQgbm90aWNlcyBzdGF0aW5nIHRoYXQgWW91IGNoYW5nZWQgdGhlIGZpbGVzOyBhbmQKCiAgICAgKGMpIFlvdSBtdXN0IHJldGFpbiwgaW4gdGhlIFNvdXJjZSBmb3JtIG9mIGFueSBEZXJpdmF0aXZlIFdvcmtzIHRoYXQgWW91IGRpc3RyaWJ1dGUsIGFsbCBjb3B5cmlnaHQsIHBhdGVudCwgdHJhZGVtYXJrLCBhbmQgYXR0cmlidXRpb24gbm90aWNlcyBmcm9tIHRoZSBTb3VyY2UgZm9ybSBvZiB0aGUgV29yaywgZXhjbHVkaW5nIHRob3NlIG5vdGljZXMgdGhhdCBkbyBub3QgcGVydGFpbiB0byBhbnkgcGFydCBvZiB0aGUgRGVyaXZhdGl2ZSBXb3JrczsgYW5kCgogICAgIChkKSBJZiB0aGUgV29yayBpbmNsdWRlcyBhICJOT1RJQ0UiIHRleHQgZmlsZSBhcyBwYXJ0IG9mIGl0cyBkaXN0cmlidXRpb24sIHRoZW4gYW55IERlcml2YXRpdmUgV29ya3MgdGhhdCBZb3UgZGlzdHJpYnV0ZSBtdXN0IGluY2x1ZGUgYSByZWFkYWJsZSBjb3B5IG9mIHRoZSBhdHRyaWJ1dGlvbiBub3RpY2VzIGNvbnRhaW5lZCB3aXRoaW4gc3VjaCBOT1RJQ0UgZmlsZSwgZXhjbHVkaW5nIHRob3NlIG5vdGljZXMgdGhhdCBkbyBub3QgcGVydGFpbiB0byBhbnkgcGFydCBvZiB0aGUgRGVyaXZhdGl2ZSBXb3JrcywgaW4gYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGxhY2VzOiB3aXRoaW4gYSBOT1RJQ0UgdGV4dCBmaWxlIGRpc3RyaWJ1dGVkIGFzIHBhcnQgb2YgdGhlIERlcml2YXRpdmUgV29ya3M7IHdpdGhpbiB0aGUgU291cmNlIGZvcm0gb3IgZG9jdW1lbnRhdGlvbiwgaWYgcHJvdmlkZWQgYWxvbmcgd2l0aCB0aGUgRGVyaXZhdGl2ZSBXb3Jrczsgb3IsIHdpdGhpbiBhIGRpc3BsYXkgZ2VuZXJhdGVkIGJ5IHRoZSBEZXJpdmF0aXZlIFdvcmtzLCBpZiBhbmQgd2hlcmV2ZXIgc3VjaCB0aGlyZC1wYXJ0eSBub3RpY2VzIG5vcm1hbGx5IGFwcGVhci4gVGhlIGNvbnRlbnRzIG9mIHRoZSBOT1RJQ0UgZmlsZSBhcmUgZm9yIGluZm9ybWF0aW9uYWwgcHVycG9zZXMgb25seSBhbmQgZG8gbm90IG1vZGlmeSB0aGUgTGljZW5zZS4gWW91IG1heSBhZGQgWW91ciBvd24gYXR0cmlidXRpb24gbm90aWNlcyB3aXRoaW4gRGVyaXZhdGl2ZSBXb3JrcyB0aGF0IFlvdSBkaXN0cmlidXRlLCBhbG9uZ3NpZGUgb3IgYXMgYW4gYWRkZW5kdW0gdG8gdGhlIE5PVElDRSB0ZXh0IGZyb20gdGhlIFdvcmssIHByb3ZpZGVkIHRoYXQgc3VjaCBhZGRpdGlvbmFsIGF0dHJpYnV0aW9uIG5vdGljZXMgY2Fubm90IGJlIGNvbnN0cnVlZCBhcyBtb2RpZnlpbmcgdGhlIExpY2Vuc2UuCgogICAgIFlvdSBtYXkgYWRkIFlvdXIgb3duIGNvcHlyaWdodCBzdGF0ZW1lbnQgdG8gWW91ciBtb2RpZmljYXRpb25zIGFuZCBtYXkgcHJvdmlkZSBhZGRpdGlvbmFsIG9yIGRpZmZlcmVudCBsaWNlbnNlIHRlcm1zIGFuZCBjb25kaXRpb25zIGZvciB1c2UsIHJlcHJvZHVjdGlvbiwgb3IgZGlzdHJpYnV0aW9uIG9mIFlvdXIgbW9kaWZpY2F0aW9ucywgb3IgZm9yIGFueSBzdWNoIERlcml2YXRpdmUgV29ya3MgYXMgYSB3aG9sZSwgcHJvdmlkZWQgWW91ciB1c2UsIHJlcHJvZHVjdGlvbiwgYW5kIGRpc3RyaWJ1dGlvbiBvZiB0aGUgV29yayBvdGhlcndpc2UgY29tcGxpZXMgd2l0aCB0aGUgY29uZGl0aW9ucyBzdGF0ZWQgaW4gdGhpcyBMaWNlbnNlLgoKNS4gU3VibWlzc2lvbiBvZiBDb250cmlidXRpb25zLiBVbmxlc3MgWW91IGV4cGxpY2l0bHkgc3RhdGUgb3RoZXJ3aXNlLCBhbnkgQ29udHJpYnV0aW9uIGludGVudGlvbmFsbHkgc3VibWl0dGVkIGZvciBpbmNsdXNpb24gaW4gdGhlIFdvcmsgYnkgWW91IHRvIHRoZSBMaWNlbnNvciBzaGFsbCBiZSB1bmRlciB0aGUgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdGhpcyBMaWNlbnNlLCB3aXRob3V0IGFueSBhZGRpdGlvbmFsIHRlcm1zIG9yIGNvbmRpdGlvbnMuIE5vdHdpdGhzdGFuZGluZyB0aGUgYWJvdmUsIG5vdGhpbmcgaGVyZWluIHNoYWxsIHN1cGVyc2VkZSBvciBtb2RpZnkgdGhlIHRlcm1zIG9mIGFueSBzZXBhcmF0ZSBsaWNlbnNlIGFncmVlbWVudCB5b3UgbWF5IGhhdmUgZXhlY3V0ZWQgd2l0aCBMaWNlbnNvciByZWdhcmRpbmcgc3VjaCBDb250cmlidXRpb25zLgoKNi4gVHJhZGVtYXJrcy4gVGhpcyBMaWNlbnNlIGRvZXMgbm90IGdyYW50IHBlcm1pc3Npb24gdG8gdXNlIHRoZSB0cmFkZSBuYW1lcywgdHJhZGVtYXJrcywgc2VydmljZSBtYXJrcywgb3IgcHJvZHVjdCBuYW1lcyBvZiB0aGUgTGljZW5zb3IsIGV4Y2VwdCBhcyByZXF1aXJlZCBmb3IgcmVhc29uYWJsZSBhbmQgY3VzdG9tYXJ5IHVzZSBpbiBkZXNjcmliaW5nIHRoZSBvcmlnaW4gb2YgdGhlIFdvcmsgYW5kIHJlcHJvZHVjaW5nIHRoZSBjb250ZW50IG9mIHRoZSBOT1RJQ0UgZmlsZS4KCjcuIERpc2NsYWltZXIgb2YgV2FycmFudHkuIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgTGljZW5zb3IgcHJvdmlkZXMgdGhlIFdvcmsgKGFuZCBlYWNoIENvbnRyaWJ1dG9yIHByb3ZpZGVzIGl0cyBDb250cmlidXRpb25zKSBvbiBhbiAiQVMgSVMiIEJBU0lTLCBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZCwgaW5jbHVkaW5nLCB3aXRob3V0IGxpbWl0YXRpb24sIGFueSB3YXJyYW50aWVzIG9yIGNvbmRpdGlvbnMgb2YgVElUTEUsIE5PTi1JTkZSSU5HRU1FTlQsIE1FUkNIQU5UQUJJTElUWSwgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuIFlvdSBhcmUgc29sZWx5IHJlc3BvbnNpYmxlIGZvciBkZXRlcm1pbmluZyB0aGUgYXBwcm9wcmlhdGVuZXNzIG9mIHVzaW5nIG9yIHJlZGlzdHJpYnV0aW5nIHRoZSBXb3JrIGFuZCBhc3N1bWUgYW55IHJpc2tzIGFzc29jaWF0ZWQgd2l0aCBZb3VyIGV4ZXJjaXNlIG9mIHBlcm1pc3Npb25zIHVuZGVyIHRoaXMgTGljZW5zZS4KCjguIExpbWl0YXRpb24gb2YgTGlhYmlsaXR5LiBJbiBubyBldmVudCBhbmQgdW5kZXIgbm8gbGVnYWwgdGhlb3J5LCB3aGV0aGVyIGluIHRvcnQgKGluY2x1ZGluZyBuZWdsaWdlbmNlKSwgY29udHJhY3QsIG9yIG90aGVyd2lzZSwgdW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IChzdWNoIGFzIGRlbGliZXJhdGUgYW5kIGdyb3NzbHkgbmVnbGlnZW50IGFjdHMpIG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzaGFsbCBhbnkgQ29udHJpYnV0b3IgYmUgbGlhYmxlIHRvIFlvdSBmb3IgZGFtYWdlcywgaW5jbHVkaW5nIGFueSBkaXJlY3QsIGluZGlyZWN0LCBzcGVjaWFsLCBpbmNpZGVudGFsLCBvciBjb25zZXF1ZW50aWFsIGRhbWFnZXMgb2YgYW55IGNoYXJhY3RlciBhcmlzaW5nIGFzIGEgcmVzdWx0IG9mIHRoaXMgTGljZW5zZSBvciBvdXQgb2YgdGhlIHVzZSBvciBpbmFiaWxpdHkgdG8gdXNlIHRoZSBXb3JrIChpbmNsdWRpbmcgYnV0IG5vdCBsaW1pdGVkIHRvIGRhbWFnZXMgZm9yIGxvc3Mgb2YgZ29vZHdpbGwsIHdvcmsgc3RvcHBhZ2UsIGNvbXB1dGVyIGZhaWx1cmUgb3IgbWFsZnVuY3Rpb24sIG9yIGFueSBhbmQgYWxsIG90aGVyIGNvbW1lcmNpYWwgZGFtYWdlcyBvciBsb3NzZXMpLCBldmVuIGlmIHN1Y2ggQ29udHJpYnV0b3IgaGFzIGJlZW4gYWR2aXNlZCBvZiB0aGUgcG9zc2liaWxpdHkgb2Ygc3VjaCBkYW1hZ2VzLgoKOS4gQWNjZXB0aW5nIFdhcnJhbnR5IG9yIEFkZGl0aW9uYWwgTGlhYmlsaXR5LiBXaGlsZSByZWRpc3RyaWJ1dGluZyB0aGUgV29yayBvciBEZXJpdmF0aXZlIFdvcmtzIHRoZXJlb2YsIFlvdSBtYXkgY2hvb3NlIHRvIG9mZmVyLCBhbmQgY2hhcmdlIGEgZmVlIGZvciwgYWNjZXB0YW5jZSBvZiBzdXBwb3J0LCB3YXJyYW50eSwgaW5kZW1uaXR5LCBvciBvdGhlciBsaWFiaWxpdHkgb2JsaWdhdGlvbnMgYW5kL29yIHJpZ2h0cyBjb25zaXN0ZW50IHdpdGggdGhpcyBMaWNlbnNlLiBIb3dldmVyLCBpbiBhY2NlcHRpbmcgc3VjaCBvYmxpZ2F0aW9ucywgWW91IG1heSBhY3Qgb25seSBvbiBZb3VyIG93biBiZWhhbGYgYW5kIG9uIFlvdXIgc29sZSByZXNwb25zaWJpbGl0eSwgbm90IG9uIGJlaGFsZiBvZiBhbnkgb3RoZXIgQ29udHJpYnV0b3IsIGFuZCBvbmx5IGlmIFlvdSBhZ3JlZSB0byBpbmRlbW5pZnksIGRlZmVuZCwgYW5kIGhvbGQgZWFjaCBDb250cmlidXRvciBoYXJtbGVzcyBmb3IgYW55IGxpYWJpbGl0eSBpbmN1cnJlZCBieSwgb3IgY2xhaW1zIGFzc2VydGVkIGFnYWluc3QsIHN1Y2ggQ29udHJpYnV0b3IgYnkgcmVhc29uIG9mIHlvdXIgYWNjZXB0aW5nIGFueSBzdWNoIHdhcnJhbnR5IG9yIGFkZGl0aW9uYWwgbGlhYmlsaXR5LgoKRU5EIE9GIFRFUk1TIEFORCBDT05ESVRJT05TCgpBUFBFTkRJWDogSG93IHRvIGFwcGx5IHRoZSBBcGFjaGUgTGljZW5zZSB0byB5b3VyIHdvcmsuCgpUbyBhcHBseSB0aGUgQXBhY2hlIExpY2Vuc2UgdG8geW91ciB3b3JrLCBhdHRhY2ggdGhlIGZvbGxvd2luZyBib2lsZXJwbGF0ZSBub3RpY2UsIHdpdGggdGhlIGZpZWxkcyBlbmNsb3NlZCBieSBicmFja2V0cyAiW10iIHJlcGxhY2VkIHdpdGggeW91ciBvd24gaWRlbnRpZnlpbmcgaW5mb3JtYXRpb24uIChEb24ndCBpbmNsdWRlIHRoZSBicmFja2V0cyEpICBUaGUgdGV4dCBzaG91bGQgYmUgZW5jbG9zZWQgaW4gdGhlIGFwcHJvcHJpYXRlIGNvbW1lbnQgc3ludGF4IGZvciB0aGUgZmlsZSBmb3JtYXQuIFdlIGFsc28gcmVjb21tZW5kIHRoYXQgYSBmaWxlIG9yIGNsYXNzIG5hbWUgYW5kIGRlc2NyaXB0aW9uIG9mIHB1cnBvc2UgYmUgaW5jbHVkZWQgb24gdGhlIHNhbWUgInByaW50ZWQgcGFnZSIgYXMgdGhlIGNvcHlyaWdodCBub3RpY2UgZm9yIGVhc2llciBpZGVudGlmaWNhdGlvbiB3aXRoaW4gdGhpcmQtcGFydHkgYXJjaGl2ZXMuCgpDb3B5cmlnaHQgW3l5eXldIFtuYW1lIG9mIGNvcHlyaWdodCBvd25lcl0KCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOwp5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuCllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdAoKaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wCgpVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlCmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuICJBUyBJUyIgQkFTSVMsCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLgpTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kCmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLgo=' + ], + url: 'https://www.apache.org/licenses/LICENSE-2.0' + ], + 'BSD-2-Clause' : [ + id: 'BSD-2-Clause', + name: 'BSD 2-Clause "Simplified" License', + text: [ + contentType: 'text/plain', + encoding: 'base64', + content: 'Q29weXJpZ2h0IDxZRUFSPiA8Q09QWVJJR0hUIEhPTERFUj4KClJlZGlzdHJpYnV0aW9uIGFuZCB1c2UgaW4gc291cmNlIGFuZCBiaW5hcnkgZm9ybXMsIHdpdGggb3Igd2l0aG91dCBtb2RpZmljYXRpb24sIGFyZSBwZXJtaXR0ZWQgcHJvdmlkZWQgdGhhdCB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnMgYXJlIG1ldDoKCjEuIFJlZGlzdHJpYnV0aW9ucyBvZiBzb3VyY2UgY29kZSBtdXN0IHJldGFpbiB0aGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lci4KCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUgZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KClRISVMgU09GVFdBUkUgSVMgUFJPVklERUQgQlkgVEhFIENPUFlSSUdIVCBIT0xERVJTIEFORCBDT05UUklCVVRPUlMg4oCcQVMgSVPigJ0gQU5EIEFOWSBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFOVElFUywgSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSBJTVBMSUVEIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBUkUgRElTQ0xBSU1FRC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIENPUFlSSUdIVCBIT0xERVIgT1IgQ09OVFJJQlVUT1JTIEJFIExJQUJMRSBGT1IgQU5ZIERJUkVDVCwgSU5ESVJFQ1QsIElOQ0lERU5UQUwsIFNQRUNJQUwsIEVYRU1QTEFSWSwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIChJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgUFJPQ1VSRU1FTlQgT0YgU1VCU1RJVFVURSBHT09EUyBPUiBTRVJWSUNFUzsgTE9TUyBPRiBVU0UsIERBVEEsIE9SIFBST0ZJVFM7IE9SIEJVU0lORVNTIElOVEVSUlVQVElPTikgSE9XRVZFUiBDQVVTRUQgQU5EIE9OIEFOWSBUSEVPUlkgT0YgTElBQklMSVRZLCBXSEVUSEVSIElOIENPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JUIChJTkNMVURJTkcgTkVHTElHRU5DRSBPUiBPVEhFUldJU0UpIEFSSVNJTkcgSU4gQU5ZIFdBWSBPVVQgT0YgVEhFIFVTRSBPRiBUSElTIFNPRlRXQVJFLCBFVkVOIElGIEFEVklTRUQgT0YgVEhFIFBPU1NJQklMSVRZIE9GIFNVQ0ggREFNQUdFLg==' + ], + url: 'https://opensource.org/license/bsd-3-clause/' + ], + 'BSD-3-Clause' : [ + id: 'BSD-3-Clause', + name: 'BSD 3-Clause "New" or "Revised" License', + text: [ + contentType: 'text/plain', + encoding: 'base64', + content: 'Q29weXJpZ2h0IChjKSA8eWVhcj4gPG93bmVyPi4gCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQgbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zIGFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCgoyLiBSZWRpc3RyaWJ1dGlvbnMgaW4gYmluYXJ5IGZvcm0gbXVzdCByZXByb2R1Y2UgdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIgaW4gdGhlIGRvY3VtZW50YXRpb24gYW5kL29yIG90aGVyIG1hdGVyaWFscyBwcm92aWRlZCB3aXRoIHRoZSBkaXN0cmlidXRpb24uCgozLiBOZWl0aGVyIHRoZSBuYW1lIG9mIHRoZSBjb3B5cmlnaHQgaG9sZGVyIG5vciB0aGUgbmFtZXMgb2YgaXRzIGNvbnRyaWJ1dG9ycyBtYXkgYmUgdXNlZCB0byBlbmRvcnNlIG9yIHByb21vdGUgcHJvZHVjdHMgZGVyaXZlZCBmcm9tIHRoaXMgc29mdHdhcmUgd2l0aG91dCBzcGVjaWZpYyBwcmlvciB3cml0dGVuIHBlcm1pc3Npb24uCgpUSElTIFNPRlRXQVJFIElTIFBST1ZJREVEIEJZIFRIRSBDT1BZUklHSFQgSE9MREVSUyBBTkQgQ09OVFJJQlVUT1JTICJBUyBJUyIgQU5EIEFOWSBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFOVElFUywgSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSBJTVBMSUVEIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBUkUgRElTQ0xBSU1FRC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIENPUFlSSUdIVCBIT0xERVIgT1IgQ09OVFJJQlVUT1JTIEJFIExJQUJMRSBGT1IgQU5ZIERJUkVDVCwgSU5ESVJFQ1QsIElOQ0lERU5UQUwsIFNQRUNJQUwsIEVYRU1QTEFSWSwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIChJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgUFJPQ1VSRU1FTlQgT0YgU1VCU1RJVFVURSBHT09EUyBPUiBTRVJWSUNFUzsgTE9TUyBPRiBVU0UsIERBVEEsIE9SIFBST0ZJVFM7IE9SIEJVU0lORVNTIElOVEVSUlVQVElPTikgSE9XRVZFUiBDQVVTRUQgQU5EIE9OIEFOWSBUSEVPUlkgT0YgTElBQklMSVRZLCBXSEVUSEVSIElOIENPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JUIChJTkNMVURJTkcgTkVHTElHRU5DRSBPUiBPVEhFUldJU0UpIEFSSVNJTkcgSU4gQU5ZIFdBWSBPVVQgT0YgVEhFIFVTRSBPRiBUSElTIFNPRlRXQVJFLCBFVkVOIElGIEFEVklTRUQgT0YgVEhFIFBPU1NJQklMSVRZIE9GIFNVQ0ggREFNQUdFLgo=' + ], + url: 'https://opensource.org/license/bsd-3-clause/' + ], + // Variant of Apache 1.1 license. Approved by legal LEGAL-707 + 'OpenSymphony' : [ + id: 'Apache-1.1', // this isn't officially recognized by SPDX, but is considered compatible with Apache 1.1 + name: 'The OpenSymphony Software License, Version 1.1', + text: [ + contentType: 'text/plain', + encoding: 'base64', + content: 'PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KVGhlIE9wZW5TeW1waG9ueSBTb2Z0d2FyZSBMaWNlbnNlLCBWZXJzaW9uIDEuMQoKKHRoaXMgbGljZW5zZSBpcyBkZXJpdmVkIGFuZCBmdWxseSBjb21wYXRpYmxlIHdpdGggdGhlIEFwYWNoZSBTb2Z0d2FyZQpMaWNlbnNlIC0gc2VlIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9MSUNFTlNFLnR4dCkKCkNvcHlyaWdodCAoYykgMjAwMSBUaGUgT3BlblN5bXBob255IEdyb3VwLiBBbGwgcmlnaHRzIHJlc2VydmVkLgoKUmVkaXN0cmlidXRpb24gYW5kIHVzZSBpbiBzb3VyY2UgYW5kIGJpbmFyeSBmb3Jtcywgd2l0aCBvciB3aXRob3V0Cm1vZGlmaWNhdGlvbiwgYXJlIHBlcm1pdHRlZCBwcm92aWRlZCB0aGF0IHRoZSBmb2xsb3dpbmcgY29uZGl0aW9ucwphcmUgbWV0OgoKMS4gUmVkaXN0cmlidXRpb25zIG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHQKICAgbm90aWNlLCB0aGlzIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlIGZvbGxvd2luZyBkaXNjbGFpbWVyLgoKMi4gUmVkaXN0cmlidXRpb25zIGluIGJpbmFyeSBmb3JtIG11c3QgcmVwcm9kdWNlIHRoZSBhYm92ZSBjb3B5cmlnaHQKICAgbm90aWNlLCB0aGlzIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlIGZvbGxvd2luZyBkaXNjbGFpbWVyIGluCiAgIHRoZSBkb2N1bWVudGF0aW9uIGFuZC9vciBvdGhlciBtYXRlcmlhbHMgcHJvdmlkZWQgd2l0aCB0aGUKICAgZGlzdHJpYnV0aW9uLgoKMy4gVGhlIGVuZC11c2VyIGRvY3VtZW50YXRpb24gaW5jbHVkZWQgd2l0aCB0aGUgcmVkaXN0cmlidXRpb24sCiAgIGlmIGFueSwgbXVzdCBpbmNsdWRlIHRoZSBmb2xsb3dpbmcgYWNrbm93bGVkZ21lbnQ6CiAgICAgICJUaGlzIHByb2R1Y3QgaW5jbHVkZXMgc29mdHdhcmUgZGV2ZWxvcGVkIGJ5IHRoZQogICAgICAgT3BlblN5bXBob255IEdyb3VwIChodHRwOi8vd3d3Lm9wZW5zeW1waG9ueS5jb20vKS4iCiAgIEFsdGVybmF0ZWx5LCB0aGlzIGFja25vd2xlZGdtZW50IG1heSBhcHBlYXIgaW4gdGhlIHNvZnR3YXJlIGl0c2VsZiwKICAgaWYgYW5kIHdoZXJldmVyIHN1Y2ggdGhpcmQtcGFydHkgYWNrbm93bGVkZ21lbnRzIG5vcm1hbGx5IGFwcGVhci4KCjQuIFRoZSBuYW1lcyAiT3BlblN5bXBob255IiBhbmQgIlRoZSBPcGVuU3ltcGhvbnkgR3JvdXAiCiAgIG11c3Qgbm90IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQgZnJvbSB0aGlzCiAgIHNvZnR3YXJlIHdpdGhvdXQgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLiBGb3Igd3JpdHRlbgogICBwZXJtaXNzaW9uLCBwbGVhc2UgY29udGFjdCBsaWNlbnNlQG9wZW5zeW1waG9ueS5jb20gLgoKNS4gUHJvZHVjdHMgZGVyaXZlZCBmcm9tIHRoaXMgc29mdHdhcmUgbWF5IG5vdCBiZSBjYWxsZWQgIk9wZW5TeW1waG9ueSIKICAgb3IgIlNpdGVNZXNoIiwgbm9yIG1heSAiT3BlblN5bXBob255IiBvciAiU2l0ZU1lc2giIGFwcGVhciBpbiB0aGVpcgogICBuYW1lLCB3aXRob3V0IHByaW9yIHdyaXR0ZW4gcGVybWlzc2lvbiBvZiB0aGUgT3BlblN5bXBob255IEdyb3VwLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBgYEFTIElTJycgQU5EIEFOWSBFWFBSRVNTRUQgT1IgSU1QTElFRApXQVJSQU5USUVTLCBJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgVEhFIElNUExJRUQgV0FSUkFOVElFUwpPRiBNRVJDSEFOVEFCSUxJVFkgQU5EIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFSRQpESVNDTEFJTUVELiAgSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFQQUNIRSBTT0ZUV0FSRSBGT1VOREFUSU9OIE9SCklUUyBDT05UUklCVVRPUlMgQkUgTElBQkxFIEZPUiBBTlkgRElSRUNULCBJTkRJUkVDVCwgSU5DSURFTlRBTCwKU1BFQ0lBTCwgRVhFTVBMQVJZLCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgKElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBQUk9DVVJFTUVOVCBPRiBTVUJTVElUVVRFIEdPT0RTIE9SIFNFUlZJQ0VTOyBMT1NTIE9GClVTRSwgREFUQSwgT1IgUFJPRklUUzsgT1IgQlVTSU5FU1MgSU5URVJSVVBUSU9OKSBIT1dFVkVSIENBVVNFRCBBTkQKT04gQU5ZIFRIRU9SWSBPRiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQ09OVFJBQ1QsIFNUUklDVCBMSUFCSUxJVFksCk9SIFRPUlQgKElOQ0xVRElORyBORUdMSUdFTkNFIE9SIE9USEVSV0lTRSkgQVJJU0lORyBJTiBBTlkgV0FZIE9VVApPRiBUSEUgVVNFIE9GIFRISVMgU09GVFdBUkUsIEVWRU4gSUYgQURWSVNFRCBPRiBUSEUgUE9TU0lCSUxJVFkgT0YKU1VDSCBEQU1BR0UuCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Cg==' + ], + url: 'https://raw.githubusercontent.com/sitemesh/sitemesh2/refs/heads/master/LICENSE.txt' + ], + 'UPL-1.0' : [ + id: 'UPL-1.0', + name: 'Universal Permissive License (UPL), Version 1.0', + text: [ + contentType: 'text/plain', + encoding: 'base64', + content: 'Q29weXJpZ2h0IChjKSBbeWVhcl0gW2NvcHlyaWdodCBob2xkZXJzXQoKVGhlIFVuaXZlcnNhbCBQZXJtaXNzaXZlIExpY2Vuc2UgKFVQTCksIFZlcnNpb24gMS4wCgpTdWJqZWN0IHRvIHRoZSBjb25kaXRpb24gc2V0IGZvcnRoIGJlbG93LCBwZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkIHRvIGFueQpwZXJzb24gb2J0YWluaW5nIGEgY29weSBvZiB0aGlzIHNvZnR3YXJlLCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gYW5kL29yIGRhdGEKKGNvbGxlY3RpdmVseSB0aGUgIlNvZnR3YXJlIiksIGZyZWUgb2YgY2hhcmdlIGFuZCB1bmRlciBhbnkgYW5kIGFsbCBjb3B5cmlnaHQKcmlnaHRzIGluIHRoZSBTb2Z0d2FyZSwgYW5kIGFueSBhbmQgYWxsIHBhdGVudCByaWdodHMgb3duZWQgb3IgZnJlZWx5CmxpY2Vuc2FibGUgYnkgZWFjaCBsaWNlbnNvciBoZXJldW5kZXIgY292ZXJpbmcgZWl0aGVyIChpKSB0aGUgdW5tb2RpZmllZApTb2Z0d2FyZSBhcyBjb250cmlidXRlZCB0byBvciBwcm92aWRlZCBieSBzdWNoIGxpY2Vuc29yLCBvciAoaWkpIHRoZSBMYXJnZXIKV29ya3MgKGFzIGRlZmluZWQgYmVsb3cpLCB0byBkZWFsIGluIGJvdGgKCihhKSB0aGUgU29mdHdhcmUsIGFuZAooYikgYW55IHBpZWNlIG9mIHNvZnR3YXJlIGFuZC9vciBoYXJkd2FyZSBsaXN0ZWQgaW4gdGhlIGxyZ3J3cmtzLnR4dCBmaWxlIGlmCm9uZSBpcyBpbmNsdWRlZCB3aXRoIHRoZSBTb2Z0d2FyZSAoZWFjaCBhICJMYXJnZXIgV29yayIgdG8gd2hpY2ggdGhlIFNvZnR3YXJlCmlzIGNvbnRyaWJ1dGVkIGJ5IHN1Y2ggbGljZW5zb3JzKSwKCndpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cyB0byBjb3B5LCBjcmVhdGUKZGVyaXZhdGl2ZSB3b3JrcyBvZiwgZGlzcGxheSwgcGVyZm9ybSwgYW5kIGRpc3RyaWJ1dGUgdGhlIFNvZnR3YXJlIGFuZCBtYWtlLAp1c2UsIHNlbGwsIG9mZmVyIGZvciBzYWxlLCBpbXBvcnQsIGV4cG9ydCwgaGF2ZSBtYWRlLCBhbmQgaGF2ZSBzb2xkIHRoZQpTb2Z0d2FyZSBhbmQgdGhlIExhcmdlciBXb3JrKHMpLCBhbmQgdG8gc3VibGljZW5zZSB0aGUgZm9yZWdvaW5nIHJpZ2h0cyBvbgplaXRoZXIgdGhlc2Ugb3Igb3RoZXIgdGVybXMuCgpUaGlzIGxpY2Vuc2UgaXMgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbjoKVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIGVpdGhlciB0aGlzIGNvbXBsZXRlIHBlcm1pc3Npb24gbm90aWNlIG9yIGF0CmEgbWluaW11bSBhIHJlZmVyZW5jZSB0byB0aGUgVVBMIG11c3QgYmUgaW5jbHVkZWQgaW4gYWxsIGNvcGllcyBvcgpzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuCgpUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUgpJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSwKRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFCkFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIKTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwKT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUKU09GVFdBUkUuCg==' + ], + url: 'https://oss.oracle.com/licenses/upl/' + ], + + ] + + def licenseMapping = [ + 'pkg:maven/org.antlr/antlr4-runtime@4.7.2?type=jar' : 'BSD-3-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 + 'pkg:maven/jline/jline@2.14.6?type=jar' : 'BSD-2-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 + 'pkg:maven/org.jline/jline@3.23.0?type=jar' : 'BSD-2-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 + 'pkg:maven/org.liquibase.ext/liquibase-hibernate5@4.27.0?type=jar' : 'Apache-2.0', // maps incorrectly because of https://github.com/liquibase/liquibase/issues/2445 & the base pom does not define a license + 'pkg:maven/com.oracle.coherence.ce/coherence-bom@25.03.1?type=pom' : 'UPL-1.0', // does not have map based on license id + 'pkg:maven/com.oracle.coherence.ce/coherence-bom@22.06.2?type=pom' : 'UPL-1.0', // does not have map based on license id + 'pkg:maven/opensymphony/sitemesh@2.6.0?type=jar' : 'OpenSymphony', // custom license approved by legal LEGAL-707 + ] + + // we don't distribute these so these licenses are considered acceptable, but we still prefer ASF licenses. + // Require a whitelist of any case of category X licenses to prevent accidental inclusion in a distributed artifact + // this list will need to be updated anytime we change versions so we can revise the licenses + def licenseExceptions = [ + 'grails-docs-core' : [ + 'pkg:maven/org.jruby/jzlib@1.1.5?type=jar': 'BSD-4-Clause', // jruby is used in asciidoc + ], + 'grails-data-hibernate5-core': [ + 'pkg:maven/org.hibernate.common/hibernate-commons-annotations@5.1.2.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 + 'pkg:maven/org.hibernate/hibernate-core-jakarta@5.6.15.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 + ], + 'grails-data-hibernate5': [ + 'pkg:maven/org.hibernate.common/hibernate-commons-annotations@5.1.2.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 + 'pkg:maven/org.hibernate/hibernate-core-jakarta@5.6.15.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 + ], + 'grails-data-hibernate5-spring-boot': [ + 'pkg:maven/org.hibernate.common/hibernate-commons-annotations@5.1.2.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 + 'pkg:maven/org.hibernate/hibernate-core-jakarta@5.6.15.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 + ], + 'grails-data-hibernate5-dbmigration': [ + 'pkg:maven/javax.xml.bind/jaxb-api@2.3.1?type=jar': 'CDDL-1.1', // api export + ], + ] + + def pickLicense = { String bomRef, List licenseChoices -> + if(!bomRef) { + throw new GradleException("No bomRef found for a dependency of ${project.name}, cannot pick license") } - def licenseIds = licenses.findAll { it instanceof Map && it.license instanceof Map && it.license.id } + logger.info('Picking license for {} from {} choices', bomRef, licenseChoices.size()) + if(licenseMapping.containsKey(bomRef)) { + // There are several reasons that cyclone will get the license wrong, usually due to upstream not publishing information or publishing it incorrectly + // see the licenseMapping map above for details + def licenseId = licenseMapping[bomRef] + logger.lifecycle('Forcing license for {} to {}', bomRef, licenseId) + + def licenseBlock = licenses[licenseId] + if(!licenseBlock) { + throw new GradleException("Cannot find license information for id ${licenseId} to use for bomRef ${bomRef} in project ${project.name}") + } + + return licenseBlock + } + + if (!(licenseChoices instanceof List) || licenseChoices.isEmpty()) { + throw new GradleException("No License was found for dependency: ${bomRef} in project ${project.name}") + } + + def licenseIds = licenseChoices.findAll { it instanceof Map && it.license instanceof Map && it.license.id } def foundLicense = preferredLicenses.find { p -> licenseIds.any { it.license.id == p } } if (foundLicense) { return licenseIds.find { it.license.id == foundLicense } } - licenses[0] // pick the first one found + def defaultLicense = licenseChoices[0] // pick the first one found + def defaultLicenseId = defaultLicense.license.id as String + if(defaultLicenseId == null) { + throw new GradleException("Could not determine License id for dependency: ${bomRef} in project ${project.name} for value ${defaultLicense}") + } + if(!(defaultLicenseId in preferredLicenses)) { + def projectLicenseExemptions = licenseExceptions[project.name] ?: [:] + def permittedLicense = projectLicenseExemptions.get(bomRef) == defaultLicenseId + if(!permittedLicense) { + throw new GradleException("Unpermitted License found for bom dependency: ${bomRef} in project ${project.name} : ${defaultLicenseId}") + } + } + + return defaultLicense } + // json schema is documented here: https://cyclonedx.org/docs/1.6/json/ def rewriteSbom = { File f -> def bom = new JsonSlurper().parse(f) @@ -103,7 +227,7 @@ sbomTask.configure { CycloneDxTask it -> def comps = (bom instanceof Map && bom.components instanceof List) ? bom.components : [] comps.each { c -> if (c instanceof Map && c.licenses instanceof List && !c.licenses.isEmpty()) { - def chosen = pickLicense(c.licenses as List) + def chosen = pickLicense(c['bom-ref'] as String, c.licenses as List) if (chosen != null) { c.licenses = [chosen] } diff --git a/grails-forge/gradle/publish-config.gradle b/grails-forge/gradle/publish-config.gradle index a30d0710c81..69b0e349b11 100644 --- a/grails-forge/gradle/publish-config.gradle +++ b/grails-forge/gradle/publish-config.gradle @@ -17,7 +17,6 @@ * under the License. */ - import org.gradle.crypto.checksum.Checksum import org.apache.grails.gradle.publish.GrailsPublishExtension diff --git a/grails-forge/gradle/sbom-config.gradle b/grails-forge/gradle/sbom-config.gradle deleted file mode 100644 index 779e6ac1f34..00000000000 --- a/grails-forge/gradle/sbom-config.gradle +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import groovy.json.JsonOutput -import groovy.json.JsonSlurper -import org.cyclonedx.gradle.CycloneDxTask -import org.cyclonedx.model.ExternalReference -import org.cyclonedx.model.LicenseChoice -import org.cyclonedx.model.License -import org.cyclonedx.model.OrganizationalContact -import org.cyclonedx.model.OrganizationalEntity -import org.cyclonedx.model.Component - -import java.nio.charset.StandardCharsets -import java.time.format.DateTimeFormatter -import java.time.temporal.ChronoUnit - -apply plugin: 'org.cyclonedx.bom' - -project.ext.setProperty('sbomOutputLocation', project.layout.buildDirectory.file("${findProperty('pomArtifactId') ?: project.name}-${projectVersion}-sbom.json")) - -def sbomTask = tasks.named('cyclonedxBom', CycloneDxTask) -sbomTask.configure { CycloneDxTask it -> - // the 2.x version of Cyclonedx uses a legacy syntax & helpers for setting inputs so the syntax below - // is required until the 3.x version is GA - it.setProjectType(findProperty('sbomProjectType') ?: Component.Type.FRAMEWORK.name()) - it.@componentName.set(findProperty('pomArtifactId') ?: project.name) - it.@organizationalEntity.set(new OrganizationalEntity().tap { - name = 'Apache Software Foundation' - urls = ['https://www.apache.org/', 'https://security.apache.org/'] - addContact(new OrganizationalContact().tap { - name = 'Apache Grails Development Team' - email = 'dev@grails.apache.org' - }) - }) - it.@licenseChoice.set(new LicenseChoice().tap { - addLicense(new License().tap { - name = 'Apache-2.0' - url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' - }) - }) - - it.@externalReferences.set([ - new ExternalReference().tap { - url = 'https://grails.apache.org/' - type = ExternalReference.Type.WEBSITE - } - ]) - - // sboms are published for the purposes of vulnerability analysis so only include the runtime classpath - it.@includeConfigs.set(['runtimeClasspath']) - it.@skipConfigs.set(['compileClasspath', 'testRuntimeClasspath']) - - // disable xml output - it.xmlOutput.unsetConvention() - - def sbomOutputLocation = findProperty('sbomOutputLocation') - it.jsonOutput.set(sbomOutputLocation.get()) - it.outputs.file(sbomOutputLocation) - - // cyclonedx does not support "choosing" the license placed in the sbom - // see: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/16 - it.doLast { - def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0'] - - def pickLicense = { List licenses -> - if (!(licenses instanceof List) || licenses.isEmpty()) { - return null - } - - def licenseIds = licenses.findAll { it instanceof Map && it.license instanceof Map && it.license.id } - def foundLicense = preferredLicenses.find { p -> licenseIds.any { it.license.id == p } } - if (foundLicense) { - return licenseIds.find { it.license.id == foundLicense } - } - - licenses[0] // pick the first one found - } - - def rewriteSbom = { File f -> - def bom = new JsonSlurper().parse(f) - - // timestamp is not reproducible: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/292 - bom.metadata.timestamp = DateTimeFormatter.ISO_INSTANT.format(buildDate.truncatedTo(ChronoUnit.SECONDS)) - - // components[*].licenses - def comps = (bom instanceof Map && bom.components instanceof List) ? bom.components : [] - comps.each { c -> - if (c instanceof Map && c.licenses instanceof List && !c.licenses.isEmpty()) { - def chosen = pickLicense(c.licenses as List) - if (chosen != null) { - c.licenses = [chosen] - } - } - } - - // force the serialNumber to be reproducible by removing it & recalculating - bom.serialNumber = '' - String withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom)) - def uuid = UUID.nameUUIDFromBytes(withOutSerial.getBytes(StandardCharsets.UTF_8.name())) - def urn = "urn:uuid:${uuid.toString()}" as String - bom.serialNumber = urn - - f.setText(JsonOutput.prettyPrint(JsonOutput.toJson(bom)), StandardCharsets.UTF_8.name()) - - logger.info('Rewrote JSON SBOM ({}) to pick preferred license', project.relativePath(f)) - } - - sbomOutputLocation.get().with { rewriteSbom(it.asFile) } - } -} - -// for now, we only publish the sbom inside of the binary jar (our bom projects won't have a jar file) -pluginManager.withPlugin('java') { - if (!project.findProperty('skipJavaComponent')) { - tasks.named('jar', Jar).configure { Jar jar -> - jar.dependsOn tasks.named('cyclonedxBom') - - jar.from(findProperty('sbomOutputLocation')) { - into('META-INF') - rename { - 'sbom.json' - } - } - } - } -} diff --git a/grails-forge/grails-cli/build.gradle b/grails-forge/grails-cli/build.gradle index 7a31e842ba8..70dbed5df33 100644 --- a/grails-forge/grails-cli/build.gradle +++ b/grails-forge/grails-cli/build.gradle @@ -79,7 +79,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') - from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') + from rootProject.layout.projectDirectory.file('../gradle/sbom-config.gradle') } // It's surprisingly hard to generate start scripts and *not* have them nested into a bin / lib directory diff --git a/grails-forge/grails-forge-cli/build.gradle b/grails-forge/grails-forge-cli/build.gradle index 27cd8fb0370..f342c0c6bab 100644 --- a/grails-forge/grails-forge-cli/build.gradle +++ b/grails-forge/grails-forge-cli/build.gradle @@ -111,7 +111,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') - from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') + from rootProject.layout.projectDirectory.file('../gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/doc-config.gradle') } diff --git a/grails-forge/grails-forge-core/build.gradle b/grails-forge/grails-forge-core/build.gradle index ca99b008ab0..2c68db26b3b 100644 --- a/grails-forge/grails-forge-core/build.gradle +++ b/grails-forge/grails-forge-core/build.gradle @@ -117,7 +117,7 @@ apply { from rootProject.layout.projectDirectory.file('gradle/java-config.gradle') from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') - from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') + from rootProject.layout.projectDirectory.file('../gradle/sbom-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/doc-config.gradle') } diff --git a/grails-gradle/bom/build.gradle b/grails-gradle/bom/build.gradle index 64c087c1fec..244e25420de 100644 --- a/grails-gradle/bom/build.gradle +++ b/grails-gradle/bom/build.gradle @@ -165,5 +165,5 @@ ext { apply { from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') - from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') + from rootProject.layout.projectDirectory.file('../gradle/sbom-config.gradle') } diff --git a/grails-gradle/common/build.gradle b/grails-gradle/common/build.gradle index f1c840bfa16..777503c185f 100644 --- a/grails-gradle/common/build.gradle +++ b/grails-gradle/common/build.gradle @@ -54,5 +54,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') - from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') + from rootProject.layout.projectDirectory.file('../gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-gradle/docs-core/build.gradle b/grails-gradle/docs-core/build.gradle index b98045ea343..b9a1806b9b3 100644 --- a/grails-gradle/docs-core/build.gradle +++ b/grails-gradle/docs-core/build.gradle @@ -92,5 +92,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') - from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') + from rootProject.layout.projectDirectory.file('../gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-gradle/gradle/sbom-config.gradle b/grails-gradle/gradle/sbom-config.gradle deleted file mode 100644 index bab4a25f055..00000000000 --- a/grails-gradle/gradle/sbom-config.gradle +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import groovy.json.JsonOutput -import groovy.json.JsonSlurper -import org.cyclonedx.gradle.CycloneDxTask -import org.cyclonedx.model.ExternalReference -import org.cyclonedx.model.LicenseChoice -import org.cyclonedx.model.License -import org.cyclonedx.model.OrganizationalContact -import org.cyclonedx.model.OrganizationalEntity -import org.cyclonedx.model.Component - -import java.nio.charset.StandardCharsets -import java.time.format.DateTimeFormatter -import java.time.temporal.ChronoUnit - -apply plugin: 'org.cyclonedx.bom' - -project.ext.setProperty('sbomOutputLocation', project.layout.buildDirectory.file("${findProperty('pomArtifactId') ?: project.name}-${projectVersion}-sbom.json")) - -def sbomTask = tasks.named('cyclonedxBom', CycloneDxTask) -sbomTask.configure { CycloneDxTask it -> - // the 2.x version of Cyclonedx uses a legacy syntax & helpers for setting inputs so the syntax below - // is required until the 3.x version is GA - it.setProjectType(findProperty('sbomProjectType') ?: Component.Type.FRAMEWORK.name()) - it.@componentName.set(findProperty('pomArtifactId') ?: project.name) - it.@organizationalEntity.set(new OrganizationalEntity().tap { - name = 'Apache Software Foundation' - urls = ['https://www.apache.org/', 'https://security.apache.org/'] - addContact(new OrganizationalContact().tap { - name = 'Apache Grails Development Team' - email = 'dev@grails.apache.org' - }) - }) - it.@licenseChoice.set(new LicenseChoice().tap { - addLicense(new License().tap { - name = 'Apache-2.0' - url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' - }) - }) - - it.@externalReferences.set([ - new ExternalReference().tap { - url = 'https://grails.apache.org/' - type = ExternalReference.Type.WEBSITE - } - ]) - - // sboms are published for the purposes of vulnerability analysis so only include the runtime classpath - it.@includeConfigs.set(['runtimeClasspath']) - it.@skipConfigs.set(['compileClasspath', 'testRuntimeClasspath']) - - // disable xml output - it.xmlOutput.unsetConvention() - - def sbomOutputLocation = findProperty('sbomOutputLocation') - it.jsonOutput.set(sbomOutputLocation.get()) - it.outputs.file(sbomOutputLocation) - - // cyclonedx does not support "choosing" the license placed in the sbom - // see: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/16 - it.doLast { - def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0'] - - def pickLicense = { List licenses -> - if (!(licenses instanceof List) || licenses.isEmpty()) { - return null - } - - def licenseIds = licenses.findAll { it instanceof Map && it.license instanceof Map && it.license.id } - def foundLicense = preferredLicenses.find { p -> licenseIds.any { it.license.id == p } } - if (foundLicense) { - return licenseIds.find { it.license.id == foundLicense } - } - - licenses[0] // pick the first one found - } - - def rewriteSbom = { File f -> - def bom = new JsonSlurper().parse(f) - - // timestamp is not reproducible: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/292 - bom.metadata.timestamp = DateTimeFormatter.ISO_INSTANT.format(buildDate.truncatedTo(ChronoUnit.SECONDS)) - - // components[*].licenses - def comps = (bom instanceof Map && bom.components instanceof List) ? bom.components : [] - comps.each { c -> - if (c instanceof Map && c.licenses instanceof List && !c.licenses.isEmpty()) { - def chosen = pickLicense(c.licenses as List) - if (chosen != null) { - c.licenses = [chosen] - } - } - } - - // force the serialNumber to be reproducible by removing it & recalculating - bom.serialNumber = '' - String withOutSerial = JsonOutput.prettyPrint(JsonOutput.toJson(bom)) - def uuid = UUID.nameUUIDFromBytes(withOutSerial.getBytes(StandardCharsets.UTF_8.name())) - def urn = "urn:uuid:${uuid.toString()}" as String - bom.serialNumber = urn - - f.setText(JsonOutput.prettyPrint(JsonOutput.toJson(bom)), StandardCharsets.UTF_8.name()) - - logger.info('Rewrote JSON SBOM ({}) to pick preferred license', project.relativePath(f)) - } - - sbomOutputLocation.get().with { rewriteSbom(it.asFile) } - } -} - -// for now, we only publish the sbom inside of the binary jar (our bom projects won't have a jar file nor will starters that just collect dependencies) -pluginManager.withPlugin('java') { - if (!project.findProperty('skipJavaComponent')) { - tasks.named('jar', Jar).configure { Jar jar -> - jar.dependsOn tasks.named('cyclonedxBom') - - jar.from(findProperty('sbomOutputLocation')) { - into('META-INF') - rename { - 'sbom.json' - } - } - } - } -} diff --git a/grails-gradle/model/build.gradle b/grails-gradle/model/build.gradle index 08c5fa86c6b..cea6b1c22e3 100644 --- a/grails-gradle/model/build.gradle +++ b/grails-gradle/model/build.gradle @@ -81,5 +81,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') - from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') + from rootProject.layout.projectDirectory.file('../gradle/sbom-config.gradle') } \ No newline at end of file diff --git a/grails-gradle/plugins/build.gradle b/grails-gradle/plugins/build.gradle index 68b8cda97bb..4e092f11f17 100644 --- a/grails-gradle/plugins/build.gradle +++ b/grails-gradle/plugins/build.gradle @@ -124,5 +124,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/code-style-config.gradle') from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') - from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') + from rootProject.layout.projectDirectory.file('../gradle/sbom-config.gradle') } diff --git a/grails-gradle/tasks/build.gradle b/grails-gradle/tasks/build.gradle index e72a1d82e69..821ce12f4dc 100644 --- a/grails-gradle/tasks/build.gradle +++ b/grails-gradle/tasks/build.gradle @@ -53,5 +53,5 @@ apply { from rootProject.layout.projectDirectory.file('gradle/docs-config.gradle') from rootProject.layout.projectDirectory.file('gradle/test-config.gradle') from rootProject.layout.projectDirectory.file('gradle/publish-config.gradle') - from rootProject.layout.projectDirectory.file('gradle/sbom-config.gradle') + from rootProject.layout.projectDirectory.file('../gradle/sbom-config.gradle') } \ No newline at end of file From 3020feb74b06c8d951b0276d585bb83e00809344 Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Fri, 19 Sep 2025 14:06:01 -0400 Subject: [PATCH 05/10] fix: map jzlib to the correct license --- gradle/sbom-config.gradle | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gradle/sbom-config.gradle b/gradle/sbom-config.gradle index ad0b516c0b7..61799ee0c81 100644 --- a/gradle/sbom-config.gradle +++ b/gradle/sbom-config.gradle @@ -144,15 +144,13 @@ sbomTask.configure { CycloneDxTask it -> 'pkg:maven/com.oracle.coherence.ce/coherence-bom@25.03.1?type=pom' : 'UPL-1.0', // does not have map based on license id 'pkg:maven/com.oracle.coherence.ce/coherence-bom@22.06.2?type=pom' : 'UPL-1.0', // does not have map based on license id 'pkg:maven/opensymphony/sitemesh@2.6.0?type=jar' : 'OpenSymphony', // custom license approved by legal LEGAL-707 + 'pkg:maven/org.jruby/jzlib@1.1.5?type=jar': 'BSD-3-Clause'// https://web.archive.org/web/20240822213507/http://www.jcraft.com/jzlib/LICENSE.txt shows it's a 3 clause ] // we don't distribute these so these licenses are considered acceptable, but we still prefer ASF licenses. // Require a whitelist of any case of category X licenses to prevent accidental inclusion in a distributed artifact // this list will need to be updated anytime we change versions so we can revise the licenses def licenseExceptions = [ - 'grails-docs-core' : [ - 'pkg:maven/org.jruby/jzlib@1.1.5?type=jar': 'BSD-4-Clause', // jruby is used in asciidoc - ], 'grails-data-hibernate5-core': [ 'pkg:maven/org.hibernate.common/hibernate-commons-annotations@5.1.2.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 'pkg:maven/org.hibernate/hibernate-core-jakarta@5.6.15.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 From 6595a4a156599066ceda9cd583932aae5ec621cf Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Mon, 22 Sep 2025 09:20:12 -0400 Subject: [PATCH 06/10] fix: add more licenses that ASF considers valid from https://www.apache.org/legal/resolved.html & run on projects that do not produce a jar file --- gradle/sbom-config.gradle | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle/sbom-config.gradle b/gradle/sbom-config.gradle index 61799ee0c81..05138edf9f4 100644 --- a/gradle/sbom-config.gradle +++ b/gradle/sbom-config.gradle @@ -77,8 +77,8 @@ sbomTask.configure { CycloneDxTask it -> // cyclonedx does not support "choosing" the license placed in the sbom // see: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/16 it.doLast { - // ordered so that first value is the most preferred - def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0', 'UPL-1.0'] + // ordered so that first value is the most preferred, this list is from https://www.apache.org/legal/resolved.html + def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0', '0BSD', 'UPL-1.0', 'CC0-1.0', 'ICU', 'Xnet', 'NCSA', 'W3C', 'Zlib', 'AFL-3.0', 'MS-PL', 'PSF-2.0', 'APAFML', 'BSL-1.0', 'WTFPL', 'Unlicense', 'HPND', 'EPICS', 'TCL'] // licenses are standardized @ https://spdx.org/licenses/ def licenses = [ @@ -250,6 +250,10 @@ sbomTask.configure { CycloneDxTask it -> // for now, we only publish the sbom inside of the binary jar (our bom projects won't have a jar file) pluginManager.withPlugin('java') { + tasks.named('assemble').configure { + dependsOn tasks.named('cyclonedxBom') + } + if (!project.findProperty('skipJavaComponent')) { tasks.named('jar', Jar).configure { Jar jar -> jar.dependsOn tasks.named('cyclonedxBom') From ab53f0e8fc7b0610d92ae015ee584648ef13d7a1 Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Mon, 22 Sep 2025 17:06:55 -0400 Subject: [PATCH 07/10] feedback: turn off license text to save on jar file sizes --- gradle/sbom-config.gradle | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/gradle/sbom-config.gradle b/gradle/sbom-config.gradle index 05138edf9f4..c5b27d895de 100644 --- a/gradle/sbom-config.gradle +++ b/gradle/sbom-config.gradle @@ -67,6 +67,9 @@ sbomTask.configure { CycloneDxTask it -> it.@includeConfigs.set(['runtimeClasspath']) it.@skipConfigs.set(['compileClasspath', 'testRuntimeClasspath']) + // turn off license text since it's base64 encoded & will inflate the jar sizes + it.@includeLicenseText.set(false) + // disable xml output it.xmlOutput.unsetConvention() @@ -84,56 +87,26 @@ sbomTask.configure { CycloneDxTask it -> def licenses = [ 'Apache-2.0' : [ id: 'Apache-2.0', - name: 'Apache License 2.0', - text: [ - contentType: 'text/plain', - encoding: 'base64', - content: 'QXBhY2hlIExpY2Vuc2UKVmVyc2lvbiAyLjAsIEphbnVhcnkgMjAwNApodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvCgpURVJNUyBBTkQgQ09ORElUSU9OUyBGT1IgVVNFLCBSRVBST0RVQ1RJT04sIEFORCBESVNUUklCVVRJT04KCjEuIERlZmluaXRpb25zLgoKIkxpY2Vuc2UiIHNoYWxsIG1lYW4gdGhlIHRlcm1zIGFuZCBjb25kaXRpb25zIGZvciB1c2UsIHJlcHJvZHVjdGlvbiwgYW5kIGRpc3RyaWJ1dGlvbiBhcyBkZWZpbmVkIGJ5IFNlY3Rpb25zIDEgdGhyb3VnaCA5IG9mIHRoaXMgZG9jdW1lbnQuCgoiTGljZW5zb3IiIHNoYWxsIG1lYW4gdGhlIGNvcHlyaWdodCBvd25lciBvciBlbnRpdHkgYXV0aG9yaXplZCBieSB0aGUgY29weXJpZ2h0IG93bmVyIHRoYXQgaXMgZ3JhbnRpbmcgdGhlIExpY2Vuc2UuCgoiTGVnYWwgRW50aXR5IiBzaGFsbCBtZWFuIHRoZSB1bmlvbiBvZiB0aGUgYWN0aW5nIGVudGl0eSBhbmQgYWxsIG90aGVyIGVudGl0aWVzIHRoYXQgY29udHJvbCwgYXJlIGNvbnRyb2xsZWQgYnksIG9yIGFyZSB1bmRlciBjb21tb24gY29udHJvbCB3aXRoIHRoYXQgZW50aXR5LiBGb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgZGVmaW5pdGlvbiwgImNvbnRyb2wiIG1lYW5zIChpKSB0aGUgcG93ZXIsIGRpcmVjdCBvciBpbmRpcmVjdCwgdG8gY2F1c2UgdGhlIGRpcmVjdGlvbiBvciBtYW5hZ2VtZW50IG9mIHN1Y2ggZW50aXR5LCB3aGV0aGVyIGJ5IGNvbnRyYWN0IG9yIG90aGVyd2lzZSwgb3IgKGlpKSBvd25lcnNoaXAgb2YgZmlmdHkgcGVyY2VudCAoNTAlKSBvciBtb3JlIG9mIHRoZSBvdXRzdGFuZGluZyBzaGFyZXMsIG9yIChpaWkpIGJlbmVmaWNpYWwgb3duZXJzaGlwIG9mIHN1Y2ggZW50aXR5LgoKIllvdSIgKG9yICJZb3VyIikgc2hhbGwgbWVhbiBhbiBpbmRpdmlkdWFsIG9yIExlZ2FsIEVudGl0eSBleGVyY2lzaW5nIHBlcm1pc3Npb25zIGdyYW50ZWQgYnkgdGhpcyBMaWNlbnNlLgoKIlNvdXJjZSIgZm9ybSBzaGFsbCBtZWFuIHRoZSBwcmVmZXJyZWQgZm9ybSBmb3IgbWFraW5nIG1vZGlmaWNhdGlvbnMsIGluY2x1ZGluZyBidXQgbm90IGxpbWl0ZWQgdG8gc29mdHdhcmUgc291cmNlIGNvZGUsIGRvY3VtZW50YXRpb24gc291cmNlLCBhbmQgY29uZmlndXJhdGlvbiBmaWxlcy4KCiJPYmplY3QiIGZvcm0gc2hhbGwgbWVhbiBhbnkgZm9ybSByZXN1bHRpbmcgZnJvbSBtZWNoYW5pY2FsIHRyYW5zZm9ybWF0aW9uIG9yIHRyYW5zbGF0aW9uIG9mIGEgU291cmNlIGZvcm0sIGluY2x1ZGluZyBidXQgbm90IGxpbWl0ZWQgdG8gY29tcGlsZWQgb2JqZWN0IGNvZGUsIGdlbmVyYXRlZCBkb2N1bWVudGF0aW9uLCBhbmQgY29udmVyc2lvbnMgdG8gb3RoZXIgbWVkaWEgdHlwZXMuCgoiV29yayIgc2hhbGwgbWVhbiB0aGUgd29yayBvZiBhdXRob3JzaGlwLCB3aGV0aGVyIGluIFNvdXJjZSBvciBPYmplY3QgZm9ybSwgbWFkZSBhdmFpbGFibGUgdW5kZXIgdGhlIExpY2Vuc2UsIGFzIGluZGljYXRlZCBieSBhIGNvcHlyaWdodCBub3RpY2UgdGhhdCBpcyBpbmNsdWRlZCBpbiBvciBhdHRhY2hlZCB0byB0aGUgd29yayAoYW4gZXhhbXBsZSBpcyBwcm92aWRlZCBpbiB0aGUgQXBwZW5kaXggYmVsb3cpLgoKIkRlcml2YXRpdmUgV29ya3MiIHNoYWxsIG1lYW4gYW55IHdvcmssIHdoZXRoZXIgaW4gU291cmNlIG9yIE9iamVjdCBmb3JtLCB0aGF0IGlzIGJhc2VkIG9uIChvciBkZXJpdmVkIGZyb20pIHRoZSBXb3JrIGFuZCBmb3Igd2hpY2ggdGhlIGVkaXRvcmlhbCByZXZpc2lvbnMsIGFubm90YXRpb25zLCBlbGFib3JhdGlvbnMsIG9yIG90aGVyIG1vZGlmaWNhdGlvbnMgcmVwcmVzZW50LCBhcyBhIHdob2xlLCBhbiBvcmlnaW5hbCB3b3JrIG9mIGF1dGhvcnNoaXAuIEZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyBMaWNlbnNlLCBEZXJpdmF0aXZlIFdvcmtzIHNoYWxsIG5vdCBpbmNsdWRlIHdvcmtzIHRoYXQgcmVtYWluIHNlcGFyYWJsZSBmcm9tLCBvciBtZXJlbHkgbGluayAob3IgYmluZCBieSBuYW1lKSB0byB0aGUgaW50ZXJmYWNlcyBvZiwgdGhlIFdvcmsgYW5kIERlcml2YXRpdmUgV29ya3MgdGhlcmVvZi4KCiJDb250cmlidXRpb24iIHNoYWxsIG1lYW4gYW55IHdvcmsgb2YgYXV0aG9yc2hpcCwgaW5jbHVkaW5nIHRoZSBvcmlnaW5hbCB2ZXJzaW9uIG9mIHRoZSBXb3JrIGFuZCBhbnkgbW9kaWZpY2F0aW9ucyBvciBhZGRpdGlvbnMgdG8gdGhhdCBXb3JrIG9yIERlcml2YXRpdmUgV29ya3MgdGhlcmVvZiwgdGhhdCBpcyBpbnRlbnRpb25hbGx5IHN1Ym1pdHRlZCB0byBMaWNlbnNvciBmb3IgaW5jbHVzaW9uIGluIHRoZSBXb3JrIGJ5IHRoZSBjb3B5cmlnaHQgb3duZXIgb3IgYnkgYW4gaW5kaXZpZHVhbCBvciBMZWdhbCBFbnRpdHkgYXV0aG9yaXplZCB0byBzdWJtaXQgb24gYmVoYWxmIG9mIHRoZSBjb3B5cmlnaHQgb3duZXIuIEZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyBkZWZpbml0aW9uLCAic3VibWl0dGVkIiBtZWFucyBhbnkgZm9ybSBvZiBlbGVjdHJvbmljLCB2ZXJiYWwsIG9yIHdyaXR0ZW4gY29tbXVuaWNhdGlvbiBzZW50IHRvIHRoZSBMaWNlbnNvciBvciBpdHMgcmVwcmVzZW50YXRpdmVzLCBpbmNsdWRpbmcgYnV0IG5vdCBsaW1pdGVkIHRvIGNvbW11bmljYXRpb24gb24gZWxlY3Ryb25pYyBtYWlsaW5nIGxpc3RzLCBzb3VyY2UgY29kZSBjb250cm9sIHN5c3RlbXMsIGFuZCBpc3N1ZSB0cmFja2luZyBzeXN0ZW1zIHRoYXQgYXJlIG1hbmFnZWQgYnksIG9yIG9uIGJlaGFsZiBvZiwgdGhlIExpY2Vuc29yIGZvciB0aGUgcHVycG9zZSBvZiBkaXNjdXNzaW5nIGFuZCBpbXByb3ZpbmcgdGhlIFdvcmssIGJ1dCBleGNsdWRpbmcgY29tbXVuaWNhdGlvbiB0aGF0IGlzIGNvbnNwaWN1b3VzbHkgbWFya2VkIG9yIG90aGVyd2lzZSBkZXNpZ25hdGVkIGluIHdyaXRpbmcgYnkgdGhlIGNvcHlyaWdodCBvd25lciBhcyAiTm90IGEgQ29udHJpYnV0aW9uLiIKCiJDb250cmlidXRvciIgc2hhbGwgbWVhbiBMaWNlbnNvciBhbmQgYW55IGluZGl2aWR1YWwgb3IgTGVnYWwgRW50aXR5IG9uIGJlaGFsZiBvZiB3aG9tIGEgQ29udHJpYnV0aW9uIGhhcyBiZWVuIHJlY2VpdmVkIGJ5IExpY2Vuc29yIGFuZCBzdWJzZXF1ZW50bHkgaW5jb3Jwb3JhdGVkIHdpdGhpbiB0aGUgV29yay4KCjIuIEdyYW50IG9mIENvcHlyaWdodCBMaWNlbnNlLiBTdWJqZWN0IHRvIHRoZSB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB0aGlzIExpY2Vuc2UsIGVhY2ggQ29udHJpYnV0b3IgaGVyZWJ5IGdyYW50cyB0byBZb3UgYSBwZXJwZXR1YWwsIHdvcmxkd2lkZSwgbm9uLWV4Y2x1c2l2ZSwgbm8tY2hhcmdlLCByb3lhbHR5LWZyZWUsIGlycmV2b2NhYmxlIGNvcHlyaWdodCBsaWNlbnNlIHRvIHJlcHJvZHVjZSwgcHJlcGFyZSBEZXJpdmF0aXZlIFdvcmtzIG9mLCBwdWJsaWNseSBkaXNwbGF5LCBwdWJsaWNseSBwZXJmb3JtLCBzdWJsaWNlbnNlLCBhbmQgZGlzdHJpYnV0ZSB0aGUgV29yayBhbmQgc3VjaCBEZXJpdmF0aXZlIFdvcmtzIGluIFNvdXJjZSBvciBPYmplY3QgZm9ybS4KCjMuIEdyYW50IG9mIFBhdGVudCBMaWNlbnNlLiBTdWJqZWN0IHRvIHRoZSB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB0aGlzIExpY2Vuc2UsIGVhY2ggQ29udHJpYnV0b3IgaGVyZWJ5IGdyYW50cyB0byBZb3UgYSBwZXJwZXR1YWwsIHdvcmxkd2lkZSwgbm9uLWV4Y2x1c2l2ZSwgbm8tY2hhcmdlLCByb3lhbHR5LWZyZWUsIGlycmV2b2NhYmxlIChleGNlcHQgYXMgc3RhdGVkIGluIHRoaXMgc2VjdGlvbikgcGF0ZW50IGxpY2Vuc2UgdG8gbWFrZSwgaGF2ZSBtYWRlLCB1c2UsIG9mZmVyIHRvIHNlbGwsIHNlbGwsIGltcG9ydCwgYW5kIG90aGVyd2lzZSB0cmFuc2ZlciB0aGUgV29yaywgd2hlcmUgc3VjaCBsaWNlbnNlIGFwcGxpZXMgb25seSB0byB0aG9zZSBwYXRlbnQgY2xhaW1zIGxpY2Vuc2FibGUgYnkgc3VjaCBDb250cmlidXRvciB0aGF0IGFyZSBuZWNlc3NhcmlseSBpbmZyaW5nZWQgYnkgdGhlaXIgQ29udHJpYnV0aW9uKHMpIGFsb25lIG9yIGJ5IGNvbWJpbmF0aW9uIG9mIHRoZWlyIENvbnRyaWJ1dGlvbihzKSB3aXRoIHRoZSBXb3JrIHRvIHdoaWNoIHN1Y2ggQ29udHJpYnV0aW9uKHMpIHdhcyBzdWJtaXR0ZWQuIElmIFlvdSBpbnN0aXR1dGUgcGF0ZW50IGxpdGlnYXRpb24gYWdhaW5zdCBhbnkgZW50aXR5IChpbmNsdWRpbmcgYSBjcm9zcy1jbGFpbSBvciBjb3VudGVyY2xhaW0gaW4gYSBsYXdzdWl0KSBhbGxlZ2luZyB0aGF0IHRoZSBXb3JrIG9yIGEgQ29udHJpYnV0aW9uIGluY29ycG9yYXRlZCB3aXRoaW4gdGhlIFdvcmsgY29uc3RpdHV0ZXMgZGlyZWN0IG9yIGNvbnRyaWJ1dG9yeSBwYXRlbnQgaW5mcmluZ2VtZW50LCB0aGVuIGFueSBwYXRlbnQgbGljZW5zZXMgZ3JhbnRlZCB0byBZb3UgdW5kZXIgdGhpcyBMaWNlbnNlIGZvciB0aGF0IFdvcmsgc2hhbGwgdGVybWluYXRlIGFzIG9mIHRoZSBkYXRlIHN1Y2ggbGl0aWdhdGlvbiBpcyBmaWxlZC4KCjQuIFJlZGlzdHJpYnV0aW9uLiBZb3UgbWF5IHJlcHJvZHVjZSBhbmQgZGlzdHJpYnV0ZSBjb3BpZXMgb2YgdGhlIFdvcmsgb3IgRGVyaXZhdGl2ZSBXb3JrcyB0aGVyZW9mIGluIGFueSBtZWRpdW0sIHdpdGggb3Igd2l0aG91dCBtb2RpZmljYXRpb25zLCBhbmQgaW4gU291cmNlIG9yIE9iamVjdCBmb3JtLCBwcm92aWRlZCB0aGF0IFlvdSBtZWV0IHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoKCiAgICAgKGEpIFlvdSBtdXN0IGdpdmUgYW55IG90aGVyIHJlY2lwaWVudHMgb2YgdGhlIFdvcmsgb3IgRGVyaXZhdGl2ZSBXb3JrcyBhIGNvcHkgb2YgdGhpcyBMaWNlbnNlOyBhbmQKCiAgICAgKGIpIFlvdSBtdXN0IGNhdXNlIGFueSBtb2RpZmllZCBmaWxlcyB0byBjYXJyeSBwcm9taW5lbnQgbm90aWNlcyBzdGF0aW5nIHRoYXQgWW91IGNoYW5nZWQgdGhlIGZpbGVzOyBhbmQKCiAgICAgKGMpIFlvdSBtdXN0IHJldGFpbiwgaW4gdGhlIFNvdXJjZSBmb3JtIG9mIGFueSBEZXJpdmF0aXZlIFdvcmtzIHRoYXQgWW91IGRpc3RyaWJ1dGUsIGFsbCBjb3B5cmlnaHQsIHBhdGVudCwgdHJhZGVtYXJrLCBhbmQgYXR0cmlidXRpb24gbm90aWNlcyBmcm9tIHRoZSBTb3VyY2UgZm9ybSBvZiB0aGUgV29yaywgZXhjbHVkaW5nIHRob3NlIG5vdGljZXMgdGhhdCBkbyBub3QgcGVydGFpbiB0byBhbnkgcGFydCBvZiB0aGUgRGVyaXZhdGl2ZSBXb3JrczsgYW5kCgogICAgIChkKSBJZiB0aGUgV29yayBpbmNsdWRlcyBhICJOT1RJQ0UiIHRleHQgZmlsZSBhcyBwYXJ0IG9mIGl0cyBkaXN0cmlidXRpb24sIHRoZW4gYW55IERlcml2YXRpdmUgV29ya3MgdGhhdCBZb3UgZGlzdHJpYnV0ZSBtdXN0IGluY2x1ZGUgYSByZWFkYWJsZSBjb3B5IG9mIHRoZSBhdHRyaWJ1dGlvbiBub3RpY2VzIGNvbnRhaW5lZCB3aXRoaW4gc3VjaCBOT1RJQ0UgZmlsZSwgZXhjbHVkaW5nIHRob3NlIG5vdGljZXMgdGhhdCBkbyBub3QgcGVydGFpbiB0byBhbnkgcGFydCBvZiB0aGUgRGVyaXZhdGl2ZSBXb3JrcywgaW4gYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGxhY2VzOiB3aXRoaW4gYSBOT1RJQ0UgdGV4dCBmaWxlIGRpc3RyaWJ1dGVkIGFzIHBhcnQgb2YgdGhlIERlcml2YXRpdmUgV29ya3M7IHdpdGhpbiB0aGUgU291cmNlIGZvcm0gb3IgZG9jdW1lbnRhdGlvbiwgaWYgcHJvdmlkZWQgYWxvbmcgd2l0aCB0aGUgRGVyaXZhdGl2ZSBXb3Jrczsgb3IsIHdpdGhpbiBhIGRpc3BsYXkgZ2VuZXJhdGVkIGJ5IHRoZSBEZXJpdmF0aXZlIFdvcmtzLCBpZiBhbmQgd2hlcmV2ZXIgc3VjaCB0aGlyZC1wYXJ0eSBub3RpY2VzIG5vcm1hbGx5IGFwcGVhci4gVGhlIGNvbnRlbnRzIG9mIHRoZSBOT1RJQ0UgZmlsZSBhcmUgZm9yIGluZm9ybWF0aW9uYWwgcHVycG9zZXMgb25seSBhbmQgZG8gbm90IG1vZGlmeSB0aGUgTGljZW5zZS4gWW91IG1heSBhZGQgWW91ciBvd24gYXR0cmlidXRpb24gbm90aWNlcyB3aXRoaW4gRGVyaXZhdGl2ZSBXb3JrcyB0aGF0IFlvdSBkaXN0cmlidXRlLCBhbG9uZ3NpZGUgb3IgYXMgYW4gYWRkZW5kdW0gdG8gdGhlIE5PVElDRSB0ZXh0IGZyb20gdGhlIFdvcmssIHByb3ZpZGVkIHRoYXQgc3VjaCBhZGRpdGlvbmFsIGF0dHJpYnV0aW9uIG5vdGljZXMgY2Fubm90IGJlIGNvbnN0cnVlZCBhcyBtb2RpZnlpbmcgdGhlIExpY2Vuc2UuCgogICAgIFlvdSBtYXkgYWRkIFlvdXIgb3duIGNvcHlyaWdodCBzdGF0ZW1lbnQgdG8gWW91ciBtb2RpZmljYXRpb25zIGFuZCBtYXkgcHJvdmlkZSBhZGRpdGlvbmFsIG9yIGRpZmZlcmVudCBsaWNlbnNlIHRlcm1zIGFuZCBjb25kaXRpb25zIGZvciB1c2UsIHJlcHJvZHVjdGlvbiwgb3IgZGlzdHJpYnV0aW9uIG9mIFlvdXIgbW9kaWZpY2F0aW9ucywgb3IgZm9yIGFueSBzdWNoIERlcml2YXRpdmUgV29ya3MgYXMgYSB3aG9sZSwgcHJvdmlkZWQgWW91ciB1c2UsIHJlcHJvZHVjdGlvbiwgYW5kIGRpc3RyaWJ1dGlvbiBvZiB0aGUgV29yayBvdGhlcndpc2UgY29tcGxpZXMgd2l0aCB0aGUgY29uZGl0aW9ucyBzdGF0ZWQgaW4gdGhpcyBMaWNlbnNlLgoKNS4gU3VibWlzc2lvbiBvZiBDb250cmlidXRpb25zLiBVbmxlc3MgWW91IGV4cGxpY2l0bHkgc3RhdGUgb3RoZXJ3aXNlLCBhbnkgQ29udHJpYnV0aW9uIGludGVudGlvbmFsbHkgc3VibWl0dGVkIGZvciBpbmNsdXNpb24gaW4gdGhlIFdvcmsgYnkgWW91IHRvIHRoZSBMaWNlbnNvciBzaGFsbCBiZSB1bmRlciB0aGUgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdGhpcyBMaWNlbnNlLCB3aXRob3V0IGFueSBhZGRpdGlvbmFsIHRlcm1zIG9yIGNvbmRpdGlvbnMuIE5vdHdpdGhzdGFuZGluZyB0aGUgYWJvdmUsIG5vdGhpbmcgaGVyZWluIHNoYWxsIHN1cGVyc2VkZSBvciBtb2RpZnkgdGhlIHRlcm1zIG9mIGFueSBzZXBhcmF0ZSBsaWNlbnNlIGFncmVlbWVudCB5b3UgbWF5IGhhdmUgZXhlY3V0ZWQgd2l0aCBMaWNlbnNvciByZWdhcmRpbmcgc3VjaCBDb250cmlidXRpb25zLgoKNi4gVHJhZGVtYXJrcy4gVGhpcyBMaWNlbnNlIGRvZXMgbm90IGdyYW50IHBlcm1pc3Npb24gdG8gdXNlIHRoZSB0cmFkZSBuYW1lcywgdHJhZGVtYXJrcywgc2VydmljZSBtYXJrcywgb3IgcHJvZHVjdCBuYW1lcyBvZiB0aGUgTGljZW5zb3IsIGV4Y2VwdCBhcyByZXF1aXJlZCBmb3IgcmVhc29uYWJsZSBhbmQgY3VzdG9tYXJ5IHVzZSBpbiBkZXNjcmliaW5nIHRoZSBvcmlnaW4gb2YgdGhlIFdvcmsgYW5kIHJlcHJvZHVjaW5nIHRoZSBjb250ZW50IG9mIHRoZSBOT1RJQ0UgZmlsZS4KCjcuIERpc2NsYWltZXIgb2YgV2FycmFudHkuIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgTGljZW5zb3IgcHJvdmlkZXMgdGhlIFdvcmsgKGFuZCBlYWNoIENvbnRyaWJ1dG9yIHByb3ZpZGVzIGl0cyBDb250cmlidXRpb25zKSBvbiBhbiAiQVMgSVMiIEJBU0lTLCBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZCwgaW5jbHVkaW5nLCB3aXRob3V0IGxpbWl0YXRpb24sIGFueSB3YXJyYW50aWVzIG9yIGNvbmRpdGlvbnMgb2YgVElUTEUsIE5PTi1JTkZSSU5HRU1FTlQsIE1FUkNIQU5UQUJJTElUWSwgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuIFlvdSBhcmUgc29sZWx5IHJlc3BvbnNpYmxlIGZvciBkZXRlcm1pbmluZyB0aGUgYXBwcm9wcmlhdGVuZXNzIG9mIHVzaW5nIG9yIHJlZGlzdHJpYnV0aW5nIHRoZSBXb3JrIGFuZCBhc3N1bWUgYW55IHJpc2tzIGFzc29jaWF0ZWQgd2l0aCBZb3VyIGV4ZXJjaXNlIG9mIHBlcm1pc3Npb25zIHVuZGVyIHRoaXMgTGljZW5zZS4KCjguIExpbWl0YXRpb24gb2YgTGlhYmlsaXR5LiBJbiBubyBldmVudCBhbmQgdW5kZXIgbm8gbGVnYWwgdGhlb3J5LCB3aGV0aGVyIGluIHRvcnQgKGluY2x1ZGluZyBuZWdsaWdlbmNlKSwgY29udHJhY3QsIG9yIG90aGVyd2lzZSwgdW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IChzdWNoIGFzIGRlbGliZXJhdGUgYW5kIGdyb3NzbHkgbmVnbGlnZW50IGFjdHMpIG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzaGFsbCBhbnkgQ29udHJpYnV0b3IgYmUgbGlhYmxlIHRvIFlvdSBmb3IgZGFtYWdlcywgaW5jbHVkaW5nIGFueSBkaXJlY3QsIGluZGlyZWN0LCBzcGVjaWFsLCBpbmNpZGVudGFsLCBvciBjb25zZXF1ZW50aWFsIGRhbWFnZXMgb2YgYW55IGNoYXJhY3RlciBhcmlzaW5nIGFzIGEgcmVzdWx0IG9mIHRoaXMgTGljZW5zZSBvciBvdXQgb2YgdGhlIHVzZSBvciBpbmFiaWxpdHkgdG8gdXNlIHRoZSBXb3JrIChpbmNsdWRpbmcgYnV0IG5vdCBsaW1pdGVkIHRvIGRhbWFnZXMgZm9yIGxvc3Mgb2YgZ29vZHdpbGwsIHdvcmsgc3RvcHBhZ2UsIGNvbXB1dGVyIGZhaWx1cmUgb3IgbWFsZnVuY3Rpb24sIG9yIGFueSBhbmQgYWxsIG90aGVyIGNvbW1lcmNpYWwgZGFtYWdlcyBvciBsb3NzZXMpLCBldmVuIGlmIHN1Y2ggQ29udHJpYnV0b3IgaGFzIGJlZW4gYWR2aXNlZCBvZiB0aGUgcG9zc2liaWxpdHkgb2Ygc3VjaCBkYW1hZ2VzLgoKOS4gQWNjZXB0aW5nIFdhcnJhbnR5IG9yIEFkZGl0aW9uYWwgTGlhYmlsaXR5LiBXaGlsZSByZWRpc3RyaWJ1dGluZyB0aGUgV29yayBvciBEZXJpdmF0aXZlIFdvcmtzIHRoZXJlb2YsIFlvdSBtYXkgY2hvb3NlIHRvIG9mZmVyLCBhbmQgY2hhcmdlIGEgZmVlIGZvciwgYWNjZXB0YW5jZSBvZiBzdXBwb3J0LCB3YXJyYW50eSwgaW5kZW1uaXR5LCBvciBvdGhlciBsaWFiaWxpdHkgb2JsaWdhdGlvbnMgYW5kL29yIHJpZ2h0cyBjb25zaXN0ZW50IHdpdGggdGhpcyBMaWNlbnNlLiBIb3dldmVyLCBpbiBhY2NlcHRpbmcgc3VjaCBvYmxpZ2F0aW9ucywgWW91IG1heSBhY3Qgb25seSBvbiBZb3VyIG93biBiZWhhbGYgYW5kIG9uIFlvdXIgc29sZSByZXNwb25zaWJpbGl0eSwgbm90IG9uIGJlaGFsZiBvZiBhbnkgb3RoZXIgQ29udHJpYnV0b3IsIGFuZCBvbmx5IGlmIFlvdSBhZ3JlZSB0byBpbmRlbW5pZnksIGRlZmVuZCwgYW5kIGhvbGQgZWFjaCBDb250cmlidXRvciBoYXJtbGVzcyBmb3IgYW55IGxpYWJpbGl0eSBpbmN1cnJlZCBieSwgb3IgY2xhaW1zIGFzc2VydGVkIGFnYWluc3QsIHN1Y2ggQ29udHJpYnV0b3IgYnkgcmVhc29uIG9mIHlvdXIgYWNjZXB0aW5nIGFueSBzdWNoIHdhcnJhbnR5IG9yIGFkZGl0aW9uYWwgbGlhYmlsaXR5LgoKRU5EIE9GIFRFUk1TIEFORCBDT05ESVRJT05TCgpBUFBFTkRJWDogSG93IHRvIGFwcGx5IHRoZSBBcGFjaGUgTGljZW5zZSB0byB5b3VyIHdvcmsuCgpUbyBhcHBseSB0aGUgQXBhY2hlIExpY2Vuc2UgdG8geW91ciB3b3JrLCBhdHRhY2ggdGhlIGZvbGxvd2luZyBib2lsZXJwbGF0ZSBub3RpY2UsIHdpdGggdGhlIGZpZWxkcyBlbmNsb3NlZCBieSBicmFja2V0cyAiW10iIHJlcGxhY2VkIHdpdGggeW91ciBvd24gaWRlbnRpZnlpbmcgaW5mb3JtYXRpb24uIChEb24ndCBpbmNsdWRlIHRoZSBicmFja2V0cyEpICBUaGUgdGV4dCBzaG91bGQgYmUgZW5jbG9zZWQgaW4gdGhlIGFwcHJvcHJpYXRlIGNvbW1lbnQgc3ludGF4IGZvciB0aGUgZmlsZSBmb3JtYXQuIFdlIGFsc28gcmVjb21tZW5kIHRoYXQgYSBmaWxlIG9yIGNsYXNzIG5hbWUgYW5kIGRlc2NyaXB0aW9uIG9mIHB1cnBvc2UgYmUgaW5jbHVkZWQgb24gdGhlIHNhbWUgInByaW50ZWQgcGFnZSIgYXMgdGhlIGNvcHlyaWdodCBub3RpY2UgZm9yIGVhc2llciBpZGVudGlmaWNhdGlvbiB3aXRoaW4gdGhpcmQtcGFydHkgYXJjaGl2ZXMuCgpDb3B5cmlnaHQgW3l5eXldIFtuYW1lIG9mIGNvcHlyaWdodCBvd25lcl0KCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOwp5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuCllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdAoKaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wCgpVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlCmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuICJBUyBJUyIgQkFTSVMsCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLgpTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kCmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLgo=' - ], url: 'https://www.apache.org/licenses/LICENSE-2.0' ], 'BSD-2-Clause' : [ id: 'BSD-2-Clause', - name: 'BSD 2-Clause "Simplified" License', - text: [ - contentType: 'text/plain', - encoding: 'base64', - content: 'Q29weXJpZ2h0IDxZRUFSPiA8Q09QWVJJR0hUIEhPTERFUj4KClJlZGlzdHJpYnV0aW9uIGFuZCB1c2UgaW4gc291cmNlIGFuZCBiaW5hcnkgZm9ybXMsIHdpdGggb3Igd2l0aG91dCBtb2RpZmljYXRpb24sIGFyZSBwZXJtaXR0ZWQgcHJvdmlkZWQgdGhhdCB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnMgYXJlIG1ldDoKCjEuIFJlZGlzdHJpYnV0aW9ucyBvZiBzb3VyY2UgY29kZSBtdXN0IHJldGFpbiB0aGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lci4KCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUgZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KClRISVMgU09GVFdBUkUgSVMgUFJPVklERUQgQlkgVEhFIENPUFlSSUdIVCBIT0xERVJTIEFORCBDT05UUklCVVRPUlMg4oCcQVMgSVPigJ0gQU5EIEFOWSBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFOVElFUywgSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSBJTVBMSUVEIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBUkUgRElTQ0xBSU1FRC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIENPUFlSSUdIVCBIT0xERVIgT1IgQ09OVFJJQlVUT1JTIEJFIExJQUJMRSBGT1IgQU5ZIERJUkVDVCwgSU5ESVJFQ1QsIElOQ0lERU5UQUwsIFNQRUNJQUwsIEVYRU1QTEFSWSwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIChJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgUFJPQ1VSRU1FTlQgT0YgU1VCU1RJVFVURSBHT09EUyBPUiBTRVJWSUNFUzsgTE9TUyBPRiBVU0UsIERBVEEsIE9SIFBST0ZJVFM7IE9SIEJVU0lORVNTIElOVEVSUlVQVElPTikgSE9XRVZFUiBDQVVTRUQgQU5EIE9OIEFOWSBUSEVPUlkgT0YgTElBQklMSVRZLCBXSEVUSEVSIElOIENPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JUIChJTkNMVURJTkcgTkVHTElHRU5DRSBPUiBPVEhFUldJU0UpIEFSSVNJTkcgSU4gQU5ZIFdBWSBPVVQgT0YgVEhFIFVTRSBPRiBUSElTIFNPRlRXQVJFLCBFVkVOIElGIEFEVklTRUQgT0YgVEhFIFBPU1NJQklMSVRZIE9GIFNVQ0ggREFNQUdFLg==' - ], url: 'https://opensource.org/license/bsd-3-clause/' ], 'BSD-3-Clause' : [ id: 'BSD-3-Clause', - name: 'BSD 3-Clause "New" or "Revised" License', - text: [ - contentType: 'text/plain', - encoding: 'base64', - content: 'Q29weXJpZ2h0IChjKSA8eWVhcj4gPG93bmVyPi4gCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQgbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zIGFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCgoyLiBSZWRpc3RyaWJ1dGlvbnMgaW4gYmluYXJ5IGZvcm0gbXVzdCByZXByb2R1Y2UgdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIgaW4gdGhlIGRvY3VtZW50YXRpb24gYW5kL29yIG90aGVyIG1hdGVyaWFscyBwcm92aWRlZCB3aXRoIHRoZSBkaXN0cmlidXRpb24uCgozLiBOZWl0aGVyIHRoZSBuYW1lIG9mIHRoZSBjb3B5cmlnaHQgaG9sZGVyIG5vciB0aGUgbmFtZXMgb2YgaXRzIGNvbnRyaWJ1dG9ycyBtYXkgYmUgdXNlZCB0byBlbmRvcnNlIG9yIHByb21vdGUgcHJvZHVjdHMgZGVyaXZlZCBmcm9tIHRoaXMgc29mdHdhcmUgd2l0aG91dCBzcGVjaWZpYyBwcmlvciB3cml0dGVuIHBlcm1pc3Npb24uCgpUSElTIFNPRlRXQVJFIElTIFBST1ZJREVEIEJZIFRIRSBDT1BZUklHSFQgSE9MREVSUyBBTkQgQ09OVFJJQlVUT1JTICJBUyBJUyIgQU5EIEFOWSBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFOVElFUywgSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSBJTVBMSUVEIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBUkUgRElTQ0xBSU1FRC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIENPUFlSSUdIVCBIT0xERVIgT1IgQ09OVFJJQlVUT1JTIEJFIExJQUJMRSBGT1IgQU5ZIERJUkVDVCwgSU5ESVJFQ1QsIElOQ0lERU5UQUwsIFNQRUNJQUwsIEVYRU1QTEFSWSwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIChJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgUFJPQ1VSRU1FTlQgT0YgU1VCU1RJVFVURSBHT09EUyBPUiBTRVJWSUNFUzsgTE9TUyBPRiBVU0UsIERBVEEsIE9SIFBST0ZJVFM7IE9SIEJVU0lORVNTIElOVEVSUlVQVElPTikgSE9XRVZFUiBDQVVTRUQgQU5EIE9OIEFOWSBUSEVPUlkgT0YgTElBQklMSVRZLCBXSEVUSEVSIElOIENPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JUIChJTkNMVURJTkcgTkVHTElHRU5DRSBPUiBPVEhFUldJU0UpIEFSSVNJTkcgSU4gQU5ZIFdBWSBPVVQgT0YgVEhFIFVTRSBPRiBUSElTIFNPRlRXQVJFLCBFVkVOIElGIEFEVklTRUQgT0YgVEhFIFBPU1NJQklMSVRZIE9GIFNVQ0ggREFNQUdFLgo=' - ], url: 'https://opensource.org/license/bsd-3-clause/' ], // Variant of Apache 1.1 license. Approved by legal LEGAL-707 'OpenSymphony' : [ - id: 'Apache-1.1', // this isn't officially recognized by SPDX, but is considered compatible with Apache 1.1 + // id is optional and the opensymphony license doesn't have an SPDX id name: 'The OpenSymphony Software License, Version 1.1', - text: [ - contentType: 'text/plain', - encoding: 'base64', - content: 'PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KVGhlIE9wZW5TeW1waG9ueSBTb2Z0d2FyZSBMaWNlbnNlLCBWZXJzaW9uIDEuMQoKKHRoaXMgbGljZW5zZSBpcyBkZXJpdmVkIGFuZCBmdWxseSBjb21wYXRpYmxlIHdpdGggdGhlIEFwYWNoZSBTb2Z0d2FyZQpMaWNlbnNlIC0gc2VlIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9MSUNFTlNFLnR4dCkKCkNvcHlyaWdodCAoYykgMjAwMSBUaGUgT3BlblN5bXBob255IEdyb3VwLiBBbGwgcmlnaHRzIHJlc2VydmVkLgoKUmVkaXN0cmlidXRpb24gYW5kIHVzZSBpbiBzb3VyY2UgYW5kIGJpbmFyeSBmb3Jtcywgd2l0aCBvciB3aXRob3V0Cm1vZGlmaWNhdGlvbiwgYXJlIHBlcm1pdHRlZCBwcm92aWRlZCB0aGF0IHRoZSBmb2xsb3dpbmcgY29uZGl0aW9ucwphcmUgbWV0OgoKMS4gUmVkaXN0cmlidXRpb25zIG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHQKICAgbm90aWNlLCB0aGlzIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlIGZvbGxvd2luZyBkaXNjbGFpbWVyLgoKMi4gUmVkaXN0cmlidXRpb25zIGluIGJpbmFyeSBmb3JtIG11c3QgcmVwcm9kdWNlIHRoZSBhYm92ZSBjb3B5cmlnaHQKICAgbm90aWNlLCB0aGlzIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlIGZvbGxvd2luZyBkaXNjbGFpbWVyIGluCiAgIHRoZSBkb2N1bWVudGF0aW9uIGFuZC9vciBvdGhlciBtYXRlcmlhbHMgcHJvdmlkZWQgd2l0aCB0aGUKICAgZGlzdHJpYnV0aW9uLgoKMy4gVGhlIGVuZC11c2VyIGRvY3VtZW50YXRpb24gaW5jbHVkZWQgd2l0aCB0aGUgcmVkaXN0cmlidXRpb24sCiAgIGlmIGFueSwgbXVzdCBpbmNsdWRlIHRoZSBmb2xsb3dpbmcgYWNrbm93bGVkZ21lbnQ6CiAgICAgICJUaGlzIHByb2R1Y3QgaW5jbHVkZXMgc29mdHdhcmUgZGV2ZWxvcGVkIGJ5IHRoZQogICAgICAgT3BlblN5bXBob255IEdyb3VwIChodHRwOi8vd3d3Lm9wZW5zeW1waG9ueS5jb20vKS4iCiAgIEFsdGVybmF0ZWx5LCB0aGlzIGFja25vd2xlZGdtZW50IG1heSBhcHBlYXIgaW4gdGhlIHNvZnR3YXJlIGl0c2VsZiwKICAgaWYgYW5kIHdoZXJldmVyIHN1Y2ggdGhpcmQtcGFydHkgYWNrbm93bGVkZ21lbnRzIG5vcm1hbGx5IGFwcGVhci4KCjQuIFRoZSBuYW1lcyAiT3BlblN5bXBob255IiBhbmQgIlRoZSBPcGVuU3ltcGhvbnkgR3JvdXAiCiAgIG11c3Qgbm90IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQgZnJvbSB0aGlzCiAgIHNvZnR3YXJlIHdpdGhvdXQgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLiBGb3Igd3JpdHRlbgogICBwZXJtaXNzaW9uLCBwbGVhc2UgY29udGFjdCBsaWNlbnNlQG9wZW5zeW1waG9ueS5jb20gLgoKNS4gUHJvZHVjdHMgZGVyaXZlZCBmcm9tIHRoaXMgc29mdHdhcmUgbWF5IG5vdCBiZSBjYWxsZWQgIk9wZW5TeW1waG9ueSIKICAgb3IgIlNpdGVNZXNoIiwgbm9yIG1heSAiT3BlblN5bXBob255IiBvciAiU2l0ZU1lc2giIGFwcGVhciBpbiB0aGVpcgogICBuYW1lLCB3aXRob3V0IHByaW9yIHdyaXR0ZW4gcGVybWlzc2lvbiBvZiB0aGUgT3BlblN5bXBob255IEdyb3VwLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBgYEFTIElTJycgQU5EIEFOWSBFWFBSRVNTRUQgT1IgSU1QTElFRApXQVJSQU5USUVTLCBJTkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgVEhFIElNUExJRUQgV0FSUkFOVElFUwpPRiBNRVJDSEFOVEFCSUxJVFkgQU5EIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFSRQpESVNDTEFJTUVELiAgSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFQQUNIRSBTT0ZUV0FSRSBGT1VOREFUSU9OIE9SCklUUyBDT05UUklCVVRPUlMgQkUgTElBQkxFIEZPUiBBTlkgRElSRUNULCBJTkRJUkVDVCwgSU5DSURFTlRBTCwKU1BFQ0lBTCwgRVhFTVBMQVJZLCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgKElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBQUk9DVVJFTUVOVCBPRiBTVUJTVElUVVRFIEdPT0RTIE9SIFNFUlZJQ0VTOyBMT1NTIE9GClVTRSwgREFUQSwgT1IgUFJPRklUUzsgT1IgQlVTSU5FU1MgSU5URVJSVVBUSU9OKSBIT1dFVkVSIENBVVNFRCBBTkQKT04gQU5ZIFRIRU9SWSBPRiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQ09OVFJBQ1QsIFNUUklDVCBMSUFCSUxJVFksCk9SIFRPUlQgKElOQ0xVRElORyBORUdMSUdFTkNFIE9SIE9USEVSV0lTRSkgQVJJU0lORyBJTiBBTlkgV0FZIE9VVApPRiBUSEUgVVNFIE9GIFRISVMgU09GVFdBUkUsIEVWRU4gSUYgQURWSVNFRCBPRiBUSEUgUE9TU0lCSUxJVFkgT0YKU1VDSCBEQU1BR0UuCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Cg==' - ], url: 'https://raw.githubusercontent.com/sitemesh/sitemesh2/refs/heads/master/LICENSE.txt' ], 'UPL-1.0' : [ id: 'UPL-1.0', - name: 'Universal Permissive License (UPL), Version 1.0', - text: [ - contentType: 'text/plain', - encoding: 'base64', - content: 'Q29weXJpZ2h0IChjKSBbeWVhcl0gW2NvcHlyaWdodCBob2xkZXJzXQoKVGhlIFVuaXZlcnNhbCBQZXJtaXNzaXZlIExpY2Vuc2UgKFVQTCksIFZlcnNpb24gMS4wCgpTdWJqZWN0IHRvIHRoZSBjb25kaXRpb24gc2V0IGZvcnRoIGJlbG93LCBwZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkIHRvIGFueQpwZXJzb24gb2J0YWluaW5nIGEgY29weSBvZiB0aGlzIHNvZnR3YXJlLCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gYW5kL29yIGRhdGEKKGNvbGxlY3RpdmVseSB0aGUgIlNvZnR3YXJlIiksIGZyZWUgb2YgY2hhcmdlIGFuZCB1bmRlciBhbnkgYW5kIGFsbCBjb3B5cmlnaHQKcmlnaHRzIGluIHRoZSBTb2Z0d2FyZSwgYW5kIGFueSBhbmQgYWxsIHBhdGVudCByaWdodHMgb3duZWQgb3IgZnJlZWx5CmxpY2Vuc2FibGUgYnkgZWFjaCBsaWNlbnNvciBoZXJldW5kZXIgY292ZXJpbmcgZWl0aGVyIChpKSB0aGUgdW5tb2RpZmllZApTb2Z0d2FyZSBhcyBjb250cmlidXRlZCB0byBvciBwcm92aWRlZCBieSBzdWNoIGxpY2Vuc29yLCBvciAoaWkpIHRoZSBMYXJnZXIKV29ya3MgKGFzIGRlZmluZWQgYmVsb3cpLCB0byBkZWFsIGluIGJvdGgKCihhKSB0aGUgU29mdHdhcmUsIGFuZAooYikgYW55IHBpZWNlIG9mIHNvZnR3YXJlIGFuZC9vciBoYXJkd2FyZSBsaXN0ZWQgaW4gdGhlIGxyZ3J3cmtzLnR4dCBmaWxlIGlmCm9uZSBpcyBpbmNsdWRlZCB3aXRoIHRoZSBTb2Z0d2FyZSAoZWFjaCBhICJMYXJnZXIgV29yayIgdG8gd2hpY2ggdGhlIFNvZnR3YXJlCmlzIGNvbnRyaWJ1dGVkIGJ5IHN1Y2ggbGljZW5zb3JzKSwKCndpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cyB0byBjb3B5LCBjcmVhdGUKZGVyaXZhdGl2ZSB3b3JrcyBvZiwgZGlzcGxheSwgcGVyZm9ybSwgYW5kIGRpc3RyaWJ1dGUgdGhlIFNvZnR3YXJlIGFuZCBtYWtlLAp1c2UsIHNlbGwsIG9mZmVyIGZvciBzYWxlLCBpbXBvcnQsIGV4cG9ydCwgaGF2ZSBtYWRlLCBhbmQgaGF2ZSBzb2xkIHRoZQpTb2Z0d2FyZSBhbmQgdGhlIExhcmdlciBXb3JrKHMpLCBhbmQgdG8gc3VibGljZW5zZSB0aGUgZm9yZWdvaW5nIHJpZ2h0cyBvbgplaXRoZXIgdGhlc2Ugb3Igb3RoZXIgdGVybXMuCgpUaGlzIGxpY2Vuc2UgaXMgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbjoKVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIGVpdGhlciB0aGlzIGNvbXBsZXRlIHBlcm1pc3Npb24gbm90aWNlIG9yIGF0CmEgbWluaW11bSBhIHJlZmVyZW5jZSB0byB0aGUgVVBMIG11c3QgYmUgaW5jbHVkZWQgaW4gYWxsIGNvcGllcyBvcgpzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuCgpUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUgpJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSwKRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFCkFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIKTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwKT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUKU09GVFdBUkUuCg==' - ], url: 'https://oss.oracle.com/licenses/upl/' ], - ] def licenseMapping = [ From fd07022a428268498cf9c00ecbd4ce56d821d3be Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Tue, 23 Sep 2025 07:15:30 -0400 Subject: [PATCH 08/10] feedback: styling & simplification --- gradle/sbom-config.gradle | 65 ++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/gradle/sbom-config.gradle b/gradle/sbom-config.gradle index c5b27d895de..d110fc0e8f9 100644 --- a/gradle/sbom-config.gradle +++ b/gradle/sbom-config.gradle @@ -39,8 +39,8 @@ def sbomTask = tasks.named('cyclonedxBom', CycloneDxTask) sbomTask.configure { CycloneDxTask it -> // the 2.x version of Cyclonedx uses a legacy syntax & helpers for setting inputs so the syntax below // is required until the 3.x version is GA - it.setProjectType(findProperty('sbomProjectType') ?: Component.Type.FRAMEWORK.name()) - it.@componentName.set(findProperty('pomArtifactId') ?: project.name) + it.setProjectType(findProperty('sbomProjectType')?.toString() ?: Component.Type.FRAMEWORK.name()) + it.@componentName.set(findProperty('pomArtifactId')?.toString() ?: project.name) it.@organizationalEntity.set(new OrganizationalEntity().tap { name = 'Apache Software Foundation' urls = ['https://www.apache.org/', 'https://security.apache.org/'] @@ -81,21 +81,23 @@ sbomTask.configure { CycloneDxTask it -> // see: https://github.com/CycloneDX/cyclonedx-gradle-plugin/issues/16 it.doLast { // ordered so that first value is the most preferred, this list is from https://www.apache.org/legal/resolved.html - def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0', '0BSD', 'UPL-1.0', 'CC0-1.0', 'ICU', 'Xnet', 'NCSA', 'W3C', 'Zlib', 'AFL-3.0', 'MS-PL', 'PSF-2.0', 'APAFML', 'BSL-1.0', 'WTFPL', 'Unlicense', 'HPND', 'EPICS', 'TCL'] + def preferredLicenses = ['Apache-2.0', 'EPL-1.0', 'BSD-3-Clause', 'EPL-2.0', 'MIT', 'MIT-0', '0BSD', 'UPL-1.0', + 'CC0-1.0', 'ICU', 'Xnet', 'NCSA', 'W3C', 'Zlib', 'AFL-3.0', 'MS-PL', 'PSF-2.0', 'APAFML', + 'BSL-1.0', 'WTFPL', 'Unlicense', 'HPND', 'EPICS', 'TCL'] // licenses are standardized @ https://spdx.org/licenses/ def licenses = [ - 'Apache-2.0' : [ - id: 'Apache-2.0', - url: 'https://www.apache.org/licenses/LICENSE-2.0' + 'Apache-2.0' : [ + id : 'Apache-2.0', + url : 'https://www.apache.org/licenses/LICENSE-2.0' ], - 'BSD-2-Clause' : [ - id: 'BSD-2-Clause', - url: 'https://opensource.org/license/bsd-3-clause/' + 'BSD-2-Clause': [ + id : 'BSD-2-Clause', + url : 'https://opensource.org/license/bsd-3-clause/' ], - 'BSD-3-Clause' : [ - id: 'BSD-3-Clause', - url: 'https://opensource.org/license/bsd-3-clause/' + 'BSD-3-Clause': [ + id : 'BSD-3-Clause', + url : 'https://opensource.org/license/bsd-3-clause/' ], // Variant of Apache 1.1 license. Approved by legal LEGAL-707 'OpenSymphony' : [ @@ -107,28 +109,29 @@ sbomTask.configure { CycloneDxTask it -> id: 'UPL-1.0', url: 'https://oss.oracle.com/licenses/upl/' ], + ] def licenseMapping = [ - 'pkg:maven/org.antlr/antlr4-runtime@4.7.2?type=jar' : 'BSD-3-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 - 'pkg:maven/jline/jline@2.14.6?type=jar' : 'BSD-2-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 - 'pkg:maven/org.jline/jline@3.23.0?type=jar' : 'BSD-2-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 - 'pkg:maven/org.liquibase.ext/liquibase-hibernate5@4.27.0?type=jar' : 'Apache-2.0', // maps incorrectly because of https://github.com/liquibase/liquibase/issues/2445 & the base pom does not define a license - 'pkg:maven/com.oracle.coherence.ce/coherence-bom@25.03.1?type=pom' : 'UPL-1.0', // does not have map based on license id - 'pkg:maven/com.oracle.coherence.ce/coherence-bom@22.06.2?type=pom' : 'UPL-1.0', // does not have map based on license id - 'pkg:maven/opensymphony/sitemesh@2.6.0?type=jar' : 'OpenSymphony', // custom license approved by legal LEGAL-707 - 'pkg:maven/org.jruby/jzlib@1.1.5?type=jar': 'BSD-3-Clause'// https://web.archive.org/web/20240822213507/http://www.jcraft.com/jzlib/LICENSE.txt shows it's a 3 clause + 'pkg:maven/org.antlr/antlr4-runtime@4.7.2?type=jar' : 'BSD-3-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 + 'pkg:maven/jline/jline@2.14.6?type=jar' : 'BSD-2-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 + 'pkg:maven/org.jline/jline@3.23.0?type=jar' : 'BSD-2-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 + 'pkg:maven/org.liquibase.ext/liquibase-hibernate5@4.27.0?type=jar': 'Apache-2.0', // maps incorrectly because of https://github.com/liquibase/liquibase/issues/2445 & the base pom does not define a license + 'pkg:maven/com.oracle.coherence.ce/coherence-bom@25.03.1?type=pom': 'UPL-1.0', // does not have map based on license id + 'pkg:maven/com.oracle.coherence.ce/coherence-bom@22.06.2?type=pom': 'UPL-1.0', // does not have map based on license id + 'pkg:maven/opensymphony/sitemesh@2.6.0?type=jar' : 'OpenSymphony', // custom license approved by legal LEGAL-707 + 'pkg:maven/org.jruby/jzlib@1.1.5?type=jar' : 'BSD-3-Clause'// https://web.archive.org/web/20240822213507/http://www.jcraft.com/jzlib/LICENSE.txt shows it's a 3 clause ] // we don't distribute these so these licenses are considered acceptable, but we still prefer ASF licenses. // Require a whitelist of any case of category X licenses to prevent accidental inclusion in a distributed artifact // this list will need to be updated anytime we change versions so we can revise the licenses def licenseExceptions = [ - 'grails-data-hibernate5-core': [ + 'grails-data-hibernate5-core' : [ 'pkg:maven/org.hibernate.common/hibernate-commons-annotations@5.1.2.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 'pkg:maven/org.hibernate/hibernate-core-jakarta@5.6.15.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 ], - 'grails-data-hibernate5': [ + 'grails-data-hibernate5' : [ 'pkg:maven/org.hibernate.common/hibernate-commons-annotations@5.1.2.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 'pkg:maven/org.hibernate/hibernate-core-jakarta@5.6.15.Final?type=jar': 'LGPL-2.1-only', // hibernate 5 is LGPL, we are migrating to ASF license in hibernate 7 ], @@ -142,19 +145,19 @@ sbomTask.configure { CycloneDxTask it -> ] def pickLicense = { String bomRef, List licenseChoices -> - if(!bomRef) { + if (!bomRef) { throw new GradleException("No bomRef found for a dependency of ${project.name}, cannot pick license") } logger.info('Picking license for {} from {} choices', bomRef, licenseChoices.size()) - if(licenseMapping.containsKey(bomRef)) { + if (licenseMapping.containsKey(bomRef)) { // There are several reasons that cyclone will get the license wrong, usually due to upstream not publishing information or publishing it incorrectly // see the licenseMapping map above for details def licenseId = licenseMapping[bomRef] logger.lifecycle('Forcing license for {} to {}', bomRef, licenseId) def licenseBlock = licenses[licenseId] - if(!licenseBlock) { + if (!licenseBlock) { throw new GradleException("Cannot find license information for id ${licenseId} to use for bomRef ${bomRef} in project ${project.name}") } @@ -173,13 +176,13 @@ sbomTask.configure { CycloneDxTask it -> def defaultLicense = licenseChoices[0] // pick the first one found def defaultLicenseId = defaultLicense.license.id as String - if(defaultLicenseId == null) { + if (defaultLicenseId == null) { throw new GradleException("Could not determine License id for dependency: ${bomRef} in project ${project.name} for value ${defaultLicense}") } - if(!(defaultLicenseId in preferredLicenses)) { + if (!(defaultLicenseId in preferredLicenses)) { def projectLicenseExemptions = licenseExceptions[project.name] ?: [:] def permittedLicense = projectLicenseExemptions.get(bomRef) == defaultLicenseId - if(!permittedLicense) { + if (!permittedLicense) { throw new GradleException("Unpermitted License found for bom dependency: ${bomRef} in project ${project.name} : ${defaultLicenseId}") } } @@ -224,12 +227,12 @@ sbomTask.configure { CycloneDxTask it -> // for now, we only publish the sbom inside of the binary jar (our bom projects won't have a jar file) pluginManager.withPlugin('java') { tasks.named('assemble').configure { - dependsOn tasks.named('cyclonedxBom') + dependsOn('cyclonedxBom') } if (!project.findProperty('skipJavaComponent')) { - tasks.named('jar', Jar).configure { Jar jar -> - jar.dependsOn tasks.named('cyclonedxBom') + tasks.named('jar').configure { Jar jar -> + jar.dependsOn('cyclonedxBom') jar.from(findProperty('sbomOutputLocation')) { into('META-INF') From 0f4f77cd145173d991f14331eb32a9e32fcf907c Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Tue, 23 Sep 2025 08:37:50 -0400 Subject: [PATCH 09/10] fix: ensure application projects are marked as applications instead of framework --- grails-forge/grails-cli/build.gradle | 1 + grails-forge/grails-forge-cli/build.gradle | 1 + grails-shell-cli/build.gradle | 4 ++++ grails-wrapper/build.gradle | 1 + 4 files changed, 7 insertions(+) diff --git a/grails-forge/grails-cli/build.gradle b/grails-forge/grails-cli/build.gradle index 70dbed5df33..21e14b13274 100644 --- a/grails-forge/grails-cli/build.gradle +++ b/grails-forge/grails-cli/build.gradle @@ -34,6 +34,7 @@ ext { ] cliProject = true startMainClass = 'org.apache.grails.cli.DelegatingShellApplication' + sbomProjectType = org.cyclonedx.model.Component.Type.APPLICATION.name() } version = projectVersion diff --git a/grails-forge/grails-forge-cli/build.gradle b/grails-forge/grails-forge-cli/build.gradle index f342c0c6bab..3a01d7bcd59 100644 --- a/grails-forge/grails-forge-cli/build.gradle +++ b/grails-forge/grails-forge-cli/build.gradle @@ -60,6 +60,7 @@ configurations { ext { cliProject = true startMainClass = 'org.grails.forge.cli.Application' + sbomProjectType = org.cyclonedx.model.Component.Type.APPLICATION.name() } dependencies { diff --git a/grails-shell-cli/build.gradle b/grails-shell-cli/build.gradle index cd70c43d950..5a5cc0e731f 100644 --- a/grails-shell-cli/build.gradle +++ b/grails-shell-cli/build.gradle @@ -31,6 +31,10 @@ configurations.register('shellCliDependencies').configure { configurations.runtimeClasspath.extendsFrom(it) } +ext { + sbomProjectType = org.cyclonedx.model.Component.Type.APPLICATION.name() +} + dependencies { implementation platform(project(':grails-bom')) diff --git a/grails-wrapper/build.gradle b/grails-wrapper/build.gradle index 8d70f1b80b4..48407350d70 100644 --- a/grails-wrapper/build.gradle +++ b/grails-wrapper/build.gradle @@ -35,6 +35,7 @@ ext { ] cliProject = true startMainClass = 'grails.init.Start' + sbomProjectType = org.cyclonedx.model.Component.Type.APPLICATION.name() } dependencies { From 83651ba47a01250104f67c870364ebb9352f252f Mon Sep 17 00:00:00 2001 From: James Daugherty Date: Tue, 23 Sep 2025 08:50:15 -0400 Subject: [PATCH 10/10] feature: include sbom metadata in MANIFEST for jar files --- gradle/sbom-config.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gradle/sbom-config.gradle b/gradle/sbom-config.gradle index d110fc0e8f9..d856d99e168 100644 --- a/gradle/sbom-config.gradle +++ b/gradle/sbom-config.gradle @@ -240,6 +240,11 @@ pluginManager.withPlugin('java') { 'sbom.json' } } + + jar.manifest { + attributes('Sbom-Location': 'META-INF/sbom.json') + attributes('Sbom-Format': 'CycloneDX') + } } } }