Repository: CycloneDX/cyclonedx-maven-plugin
Pull request #422 (2023-11-06 11:33:12)

Bump org.junit:junit-bom from 5.10.0 to 5.10.1
Bumps [org.junit:junit-bom](https://github.com/junit-team/junit5) from 5.10.0 to 5.10.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/junit-team/junit5/releases">org.junit:junit-bom's releases</a>.</em></p>
<blockquote>
<p>JUnit 5.10.1 = Platform 1.10.1 + Jupiter 5.10.1 + Vintage 5.10.1</p>
<p>See <a href="http://junit.org/junit5/docs/5.10.1/release-notes/">Release Notes</a>.</p>
<p><strong>Full Changelog</strong>: <a href="https://github.com/junit-team/junit5/compare/r5.10.0...r5.10.1">https://github.com/junit-team/junit5/compare/r5.10.0...r5.10.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/junit-team/junit5/commit/e5f50d8720741623915979ac154b65bcbcd6a577"><code>e5f50d8</code></a> Release 5.10.1</li>
<li><a href="https://github.com/junit-team/junit5/commit/ac86d18e9b15dbebe046e82743ac7f9534a17582"><code>ac86d18</code></a> Fix typo in AfterAll documentation</li>
<li><a href="https://github.com/junit-team/junit5/commit/388c5beaf42944961ab5b455c900d958a6e15588"><code>388c5be</code></a> Harmonize application of method and field filters in search algorithms</li>
<li><a href="https://github.com/junit-team/junit5/commit/f82dd1e716f8717e012152b1d1d5cc0da10d33cd"><code>f82dd1e</code></a> Apply field predicate before searching type hierarchy</li>
<li><a href="https://github.com/junit-team/junit5/commit/1d1eb8571552bbf28e578241965010de6c8ee779"><code>1d1eb85</code></a> Polishing</li>
<li><a href="https://github.com/junit-team/junit5/commit/5ce280eff69b43759a3cb0c176207abe0a41b579"><code>5ce280e</code></a> Update picocli to 4.7.5 and enable help width computation</li>
<li><a href="https://github.com/junit-team/junit5/commit/fea05c3aa80de76686f326b5ce26ddf7f153ff5a"><code>fea05c3</code></a> Fix ConsoleLauncherTests and StandaloneTests</li>
<li><a href="https://github.com/junit-team/junit5/commit/c5567354c2556e772f8a0035ef7647161011d1c0"><code>c556735</code></a> Use same expected files for all JDK versions</li>
<li><a href="https://github.com/junit-team/junit5/commit/808493ab09b30970b506a48fda3d616ac1ba4fff"><code>808493a</code></a> Run StandaloneTests for Java 8 under Java 8</li>
<li><a href="https://github.com/junit-team/junit5/commit/9ec57661c78c3889db004ab6a89416e56a2fb2e0"><code>9ec5766</code></a> Unify messages about exit codes in StandaloneTests</li>
<li>Additional commits viewable in <a href="https://github.com/junit-team/junit5/compare/r5.10.0...r5.10.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.junit:junit-bom&package-manager=maven&previous-version=5.10.0&new-version=5.10.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

> **Note**
> Automatic rebases have been disabled on this pull request as it has been open for over 30 days.



Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 8332ef3..9d76359 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -4,6 +4,10 @@ updates:
     directory: "/"
     schedule:
       interval: "daily"
+    ignore:
+      # JsonUnit `2.x` is the last Java 8 compatible major release
+      - dependency-name: "net.javacrumbs.json-unit:*"
+        update-types: ["version-update:semver-major"]
 
   - package-ecosystem: "github-actions"
     directory: "/"
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index a95be10..88ba90a 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -10,7 +10,7 @@ jobs:
     steps:
     - uses: actions/checkout@v4.1.1
     - name: Set up JDK 1.8
-      uses: actions/setup-java@v3
+      uses: actions/setup-java@v4
       with:
         distribution: 'adopt'
         java-version: '8'
diff --git a/pom.xml b/pom.xml
index 17799cf..fa7bf65 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,7 +70,7 @@
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <project.build.outputTimestamp>2023-10-30T00:44:18Z</project.build.outputTimestamp>
         <junit.version>4.13.2</junit.version>
-        <junit5.version>5.10.0</junit5.version>
+        <junit5.version>5.10.1</junit5.version>
     </properties>
 
     <scm>
@@ -203,6 +203,12 @@
             <artifactId>junit-vintage-engine</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>net.javacrumbs.json-unit</groupId>
+            <artifactId>json-unit-assertj</artifactId>
+            <version>2.38.0</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <dependencyManagement>
@@ -321,7 +327,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-javadoc-plugin</artifactId>
-                    <version>3.6.0</version>
+                    <version>3.6.3</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
@@ -436,7 +442,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-plugin-report-plugin</artifactId>
-                <version>3.10.1</version>
+                <version>3.10.2</version>
             </plugin>
         </plugins>
     </reporting>
diff --git a/src/it/makeBom/verify.groovy b/src/it/makeBom/verify.groovy
index 28f59f0..4f54802 100644
--- a/src/it/makeBom/verify.groovy
+++ b/src/it/makeBom/verify.groovy
@@ -9,7 +9,6 @@ assert bomFileXml.text.contains('<reference type="website"><url>https://github.c
 assert !bomFileXml.text.contains('<property name="maven.optional.unused">')
 
 // Reproducible Builds
-assert !bomFileJson.text.contains('"serialNumber"')
 assert !bomFileJson.text.contains('"timestamp"')
 assert bomFileJson.text.contains('"name" : "cdx:reproducible",')
 assert bomFileJson.text.contains('"value" : "enabled"')
diff --git a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java
index 4b50480..34ba8a0 100644
--- a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java
+++ b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java
@@ -334,7 +334,6 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
 
             if (outputTimestamp != null) {
                 // activate Reproducible Builds mode
-                includeBomSerialNumber = false;
                 metadata.setTimestamp(null);
                 if (schemaVersion().getVersion() >= 1.3) {
                     metadata.addProperty(newProperty("cdx:reproducible", "enabled"));
@@ -342,7 +341,8 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
             }
 
             if (schemaVersion().getVersion() >= 1.1 && includeBomSerialNumber) {
-                bom.setSerialNumber("urn:uuid:" + UUID.randomUUID());
+                String serialNumber = generateSerialNumber(metadata.getProperties());
+                bom.setSerialNumber(serialNumber);
             }
 
             if (schemaVersion().getVersion() >= 1.2) {
@@ -371,6 +371,21 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
         }
     }
 
+    private String generateSerialNumber(List<Property> properties) {
+        String gav = String.format("%s:%s:%s", project.getGroupId(), project.getArtifactId(), project.getVersion());
+        StringBuilder sb = new StringBuilder(gav);
+        if (properties != null) {
+            for(Property prop: properties) {
+                sb.append(';');
+                sb.append(prop.getName());
+                sb.append('=');
+                sb.append(prop.getValue());
+            }
+        }
+        UUID uuid = UUID.nameUUIDFromBytes(sb.toString().getBytes(StandardCharsets.UTF_8));
+        return String.format("urn:uuid:%s", uuid);
+    }
+
     private void saveBom(Bom bom) throws ParserConfigurationException, IOException, GeneratorException,
             MojoExecutionException {
         if ("all".equalsIgnoreCase(outputFormat) || "xml".equalsIgnoreCase(outputFormat)) {


Test patch:
diff --git a/src/test/java/org/cyclonedx/maven/Issue420Test.java b/src/test/java/org/cyclonedx/maven/Issue420Test.java
new file mode 100644
index 0000000..9638dcd
--- /dev/null
+++ b/src/test/java/org/cyclonedx/maven/Issue420Test.java
@@ -0,0 +1,73 @@
+package org.cyclonedx.maven;
+
+import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder;
+import io.takari.maven.testing.executor.MavenVersions;
+import io.takari.maven.testing.executor.junit.MavenJUnitTestRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
+
+/**
+ * Verifies that serial numbers are generated deterministically.
+ *
+ * @see <a href="https://github.com/CycloneDX/cyclonedx-maven-plugin/issues/420">Issue 420</a>
+ */
+@RunWith(MavenJUnitTestRunner.class)
+@MavenVersions({"3.6.3"})
+public class Issue420Test extends BaseMavenVerifier {
+
+    public Issue420Test(MavenRuntimeBuilder runtimeBuilder) throws Exception {
+        super(runtimeBuilder);
+    }
+
+    @Test
+    public void testDefaults() throws Exception {
+        test(new String[0], "urn:uuid:af111a48-2091-3e2e-ad2e-60b1975b651d");
+    }
+
+    @Test
+    public void testDefaultsWhenSerialNumberIsDisabled() throws Exception {
+        test(new String[]{"-DincludeBomSerialNumber=false"}, null);
+    }
+
+    @Test
+    public void testWhenOutputTimestampIsSet() throws Exception {
+        test(new String[]{"-Dproject.build.outputTimestamp=2023-11-08T00:00:00Z"}, "urn:uuid:3e383c4c-ef61-3eba-8214-3ecd46c4bbee");
+    }
+
+    @Test
+    public void testWhenOutputTimestampIsSetAndSerialNumberIsDisabled() throws Exception {
+        test(new String[]{"-Dproject.build.outputTimestamp=2023-11-08T00:00:00Z", "-DincludeBomSerialNumber=false"}, null);
+    }
+
+    private void test(String[] cliOptions, @Nullable String expectedSerialNumber) throws Exception {
+        File projDir = resources.getBasedir("issue-420");
+        verifier
+                .forProject(projDir)
+                .withCliOption("-Dcurrent.version=" + getCurrentVersion())
+                .withCliOption("-B")
+                .withCliOption("-Dmaven.test.skip")
+                .withCliOptions(cliOptions)
+                .execute("clean", "verify")
+                .assertErrorFreeLog();
+        assertSerialNumber(projDir, expectedSerialNumber);
+    }
+
+    private static void assertSerialNumber(File projDir, @Nullable String expectedSerialNumber) throws Exception {
+        File bomFile = new File(projDir, "target/bom.json");
+        String bomJson = new String(Files.readAllBytes(bomFile.toPath()), StandardCharsets.UTF_8);
+        String serialNumberKey = "serialNumber";
+        if (expectedSerialNumber == null) {
+            assertThatJson(bomJson).isObject().doesNotContainKey(serialNumberKey);
+        } else {
+            assertThatJson(bomJson).isObject().containsEntry(serialNumberKey, expectedSerialNumber);
+        }
+    }
+
+}
diff --git a/src/test/resources/issue-420/module-A/pom.xml b/src/test/resources/issue-420/module-A/pom.xml
new file mode 100644
index 0000000..f4244d8
--- /dev/null
+++ b/src/test/resources/issue-420/module-A/pom.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.example</groupId>
+        <artifactId>issue-420</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>issue-420-module-A</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.1</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/src/test/resources/issue-420/pom.xml b/src/test/resources/issue-420/pom.xml
new file mode 100644
index 0000000..efae20a
--- /dev/null
+++ b/src/test/resources/issue-420/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.example</groupId>
+    <artifactId>issue-420</artifactId>
+    <packaging>pom</packaging>
+    <version>${revision}</version>
+
+    <licenses>
+        <license>
+            <name>Apache-2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+        </license>
+    </licenses>
+
+    <modules>
+        <module>module-A</module>
+    </modules>
+
+    <properties>
+        <revision>1.0.0</revision>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.cyclonedx</groupId>
+                <artifactId>cyclonedx-maven-plugin</artifactId>
+                <version>${current.version}</version>
+                <executions>
+                    <execution>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>makeAggregateBom</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <outputFormat>json</outputFormat>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>