Repository: CycloneDX/cyclonedx-maven-plugin
Pull request #573 (2024-10-30 12:24:39)

fixes CycloneDX/cyclonedx-maven-plugin#382
Ignoring license information with empty name and url (in pom.xml), for example:

```
    <licenses>
        <license>
            <name/>
            <url/>
            <distribution/>
        </license>
    </licenses>
```

As CycloneDX/cyclonedx-maven-plugin#382 states, processing such incomplete license information produced invalid SBOMs.



Patch:
diff --git a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java
index a3b2220..92a32cd 100644
--- a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java
+++ b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java
@@ -222,8 +222,8 @@ public class DefaultModelConverter implements ModelConverter {
             component.setDescription(project.getDescription());
         }
         if (component.getLicenseChoice() == null || component.getLicenseChoice().getLicenses() == null || component.getLicenseChoice().getLicenses().isEmpty()) {
-            // If we don't already have license information, retrieve it.
-            if (project.getLicenses() != null) {
+            // If we don't already have license information, retrieve it, as long as it is not empty.
+            if (project.getLicenses() != null && project.getLicenses().stream().anyMatch(l -> !isLicenseBlank(l))) {
                 component.setLicenseChoice(resolveMavenLicenses(project.getLicenses(), schemaVersion, includeLicenseText));
             }
         }
@@ -425,4 +425,9 @@ public class DefaultModelConverter implements ModelConverter {
     private static boolean isURLBlank(String url) {
         return url == null || url.isEmpty() || url.trim().length() == 0;
     }
+
+    private static boolean isLicenseBlank(org.apache.maven.model.License license) {
+        return (license.getName() == null || license.getName().isEmpty() || license.getName().trim().length() == 0)
+                && (license.getUrl() == null || license.getUrl().isEmpty() || license.getUrl().trim().length() == 0);
+    }
 }


Test patch:
diff --git a/src/test/java/org/cyclonedx/maven/Issue382Test.java b/src/test/java/org/cyclonedx/maven/Issue382Test.java
new file mode 100644
index 0000000..a88bcbb
--- /dev/null
+++ b/src/test/java/org/cyclonedx/maven/Issue382Test.java
@@ -0,0 +1,47 @@
+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 java.io.File;
+import java.io.IOException;
+
+import static io.takari.maven.testing.TestResources.assertFilesPresent;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Test for <a href="https://github.com/CycloneDX/cyclonedx-maven-plugin/issues/382">issue #382</a>:
+ * Plugin does not gracefully handle present, but empty license data
+ */
+@RunWith(MavenJUnitTestRunner.class)
+@MavenVersions({"3.6.3"})
+public class Issue382Test extends BaseMavenVerifier {
+
+    public Issue382Test(MavenRuntimeBuilder runtimeBuilder) throws Exception {
+        super(runtimeBuilder);
+    }
+
+    @Test
+    public void test() throws Exception {
+        File projDir = resources.getBasedir("issue-382");
+
+        verifier
+                .forProject(projDir)
+                .withCliOption("-Dcurrent.version=" + getCurrentVersion()) // inject cyclonedx-maven-plugin version
+                .withCliOption("-X") // debug
+                .withCliOption("-B")
+                .execute("clean", "verify")
+                .assertErrorFreeLog();
+
+        assertFileNotContains(projDir, "target/bom.xml", "The BOM does not conform to the CycloneDX BOM standard");
+    }
+
+    private static void assertFileNotContains(File basedir, String expectedFile, String expectedContent) throws IOException {
+        assertFilesPresent(basedir, expectedFile);
+        String bomContents = fileRead(new File(basedir, expectedFile), true);
+        assertFalse(String.format("%s contains %s", expectedFile, expectedContent), bomContents.contains(expectedContent));
+    }
+}
diff --git a/src/test/resources/issue-382/pom.xml b/src/test/resources/issue-382/pom.xml
new file mode 100644
index 0000000..15d900f
--- /dev/null
+++ b/src/test/resources/issue-382/pom.xml
@@ -0,0 +1,61 @@
+<?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-382</artifactId>
+    <packaging>jar</packaging>
+    <version>1.0.0</version>
+
+    <name>Issue-64</name>
+
+    <licenses>
+        <license>
+            <name/>
+            <url/>
+            <distribution/>
+        </license>
+    </licenses>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency> <!-- has empty license information -->
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-ram</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+    </dependencies>
+
+    <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>
+                    <projectType>library</projectType>
+                    <schemaVersion>1.6</schemaVersion>
+                    <includeLicenseText>true</includeLicenseText>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>