diff --git a/build-plugin/spring-boot-gradle-plugin/build.gradle b/build-plugin/spring-boot-gradle-plugin/build.gradle index 43477d97dfd..99058c2c1d2 100644 --- a/build-plugin/spring-boot-gradle-plugin/build.gradle +++ b/build-plugin/spring-boot-gradle-plugin/build.gradle @@ -51,7 +51,8 @@ dependencies { optional("org.graalvm.buildtools:native-gradle-plugin") optional("org.cyclonedx:cyclonedx-gradle-plugin") { - exclude(group: "org.apache.maven", module: "maven-core") + exclude(group: "javax.annotation", module: "javax.annotation-api") + exclude(group: "javax.inject", module: "javax.inject") } optional("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") diff --git a/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/CycloneDxPluginAction.java b/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/CycloneDxPluginAction.java index 3217311a9cd..83f241220ea 100644 --- a/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/CycloneDxPluginAction.java +++ b/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/plugin/CycloneDxPluginAction.java @@ -95,19 +95,21 @@ final class CycloneDxPluginAction implements PluginApplicationAction { } private void configureBootJarTask(BootJar task, TaskProvider cycloneDxTaskProvider) { - configureJarTask(task, cycloneDxTaskProvider); + configureJarTask(task, cycloneDxTaskProvider, ""); } private void configureBootWarTask(BootWar task, TaskProvider cycloneDxTaskProvider) { - configureJarTask(task, cycloneDxTaskProvider); + configureJarTask(task, cycloneDxTaskProvider, "WEB-INF/classes/"); } - private void configureJarTask(Jar task, TaskProvider cycloneDxTaskProvider) { + private void configureJarTask(Jar task, TaskProvider cycloneDxTaskProvider, + String sbomLocationPrefix) { Provider sbomFileName = cycloneDxTaskProvider.map((cycloneDxTask) -> "META-INF/sbom/" + cycloneDxTask.getOutputName().get() + getSbomExtension(cycloneDxTask)); task.manifest((manifest) -> { manifest.getAttributes().put("Sbom-Format", "CycloneDX"); - manifest.getAttributes().put("Sbom-Location", sbomFileName); + manifest.getAttributes() + .put("Sbom-Location", sbomFileName.map((fileName) -> sbomLocationPrefix + fileName)); }); } diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/CycloneDxPluginActionIntegrationTests.java b/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/CycloneDxPluginActionIntegrationTests.java new file mode 100644 index 00000000000..29d30031bc2 --- /dev/null +++ b/build-plugin/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/plugin/CycloneDxPluginActionIntegrationTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed 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. + */ + +package org.springframework.boot.gradle.plugin; + +import java.io.File; +import java.io.IOException; +import java.util.jar.JarFile; + +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.jupiter.api.TestTemplate; + +import org.springframework.boot.gradle.junit.GradleCompatibility; +import org.springframework.boot.testsupport.gradle.testkit.GradleBuild; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link CycloneDxPluginAction}. + * + * @author Andy Wilkinson + */ +@GradleCompatibility +class CycloneDxPluginActionIntegrationTests { + + GradleBuild gradleBuild; + + @TestTemplate + void sbomIsIncludedInUberJar() throws IOException { + sbomIsIncludedInUberArchive("bootJar", ""); + } + + @TestTemplate + void sbomIsIncludedInUberWar() throws IOException { + sbomIsIncludedInUberArchive("bootWar", "WEB-INF/classes/"); + } + + private void sbomIsIncludedInUberArchive(String taskName, String sbomLocationPrefix) throws IOException { + BuildResult result = this.gradleBuild.expectDeprecationWarningsWithAtLeastVersion("7.6.6").build(taskName); + assertThat(result.task(":cyclonedxBom").getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + File[] libs = new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles(); + assertThat(libs).hasSize(1); + try (JarFile jar = new JarFile(libs[0])) { + assertThat(jar.getManifest().getMainAttributes().getValue("Sbom-Format")).isEqualTo("CycloneDX"); + String sbomLocation = jar.getManifest().getMainAttributes().getValue("Sbom-Location"); + assertThat(sbomLocation).isEqualTo(sbomLocationPrefix + "META-INF/sbom/application.cdx.json"); + assertThat(jar.getEntry(sbomLocation)).isNotNull(); + } + } + +} diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/CycloneDxPluginActionIntegrationTests-sbomIsIncludedInUberJar.gradle b/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/CycloneDxPluginActionIntegrationTests-sbomIsIncludedInUberJar.gradle new file mode 100644 index 00000000000..abd636f320e --- /dev/null +++ b/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/CycloneDxPluginActionIntegrationTests-sbomIsIncludedInUberJar.gradle @@ -0,0 +1,29 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed 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. + */ + +plugins { + id 'org.springframework.boot' version '{version}' + id 'java' +} + +apply plugin: 'org.cyclonedx.bom' + +group = 'com.example' +version = '0.0.1' + +tasks.named("bootJar") { + mainClass = 'com.example.ExampleApplication' +} diff --git a/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/CycloneDxPluginActionIntegrationTests-sbomIsIncludedInUberWar.gradle b/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/CycloneDxPluginActionIntegrationTests-sbomIsIncludedInUberWar.gradle new file mode 100644 index 00000000000..764c9d716a4 --- /dev/null +++ b/build-plugin/spring-boot-gradle-plugin/src/test/resources/org/springframework/boot/gradle/plugin/CycloneDxPluginActionIntegrationTests-sbomIsIncludedInUberWar.gradle @@ -0,0 +1,29 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed 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. + */ + +plugins { + id 'org.springframework.boot' version '{version}' + id 'war' +} + +apply plugin: 'org.cyclonedx.bom' + +group = 'com.example' +version = '0.0.1' + +tasks.named("bootWar") { + mainClass = 'com.example.ExampleApplication' +}