Repository: CycloneDX/cyclonedx-maven-plugin
Pull request #571 (2024-10-24 11:43:37)

Bump actions/checkout from 4.2.1 to 4.2.2
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.1 to 4.2.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v4.2.2</h2>
<h2>What's Changed</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment variables by <a href="https://github.com/jww3"><code>@​jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a href="https://github.com/jww3"><code>@​jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.2.1...v4.2.2">https://github.com/actions/checkout/compare/v4.2.1...v4.2.2</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h2>v4.2.2</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment variables by <a href="https://github.com/jww3"><code>@​jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a href="https://github.com/jww3"><code>@​jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/11bd71901bbe5b1630ceea73d27597364c9af683"><code>11bd719</code></a> Prepare 4.2.2 Release (<a href="https://redirect.github.com/actions/checkout/issues/1953">#1953</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/e3d2460bbb42d7710191569f88069044cfb9d8cf"><code>e3d2460</code></a> Expand unit test coverage (<a href="https://redirect.github.com/actions/checkout/issues/1946">#1946</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/163217dfcd28294438ea1c1c149cfaf66eec283e"><code>163217d</code></a> <code>url-helper.ts</code> now leverages well-known environment variables. (<a href="https://redirect.github.com/actions/checkout/issues/1941">#1941</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/v4.2.1...v4.2.2">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.2.1&new-version=4.2.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually 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>


Patch:
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 83893cb..bcc3846 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -8,7 +8,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v4.2.1
+    - uses: actions/checkout@v4.2.2
     - name: Set up JDK 1.8
       uses: actions/setup-java@v4
       with:
diff --git a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java
index dc05ed8..fe6f21f 100644
--- a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java
+++ b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java
@@ -61,6 +61,8 @@ import java.util.Set;
 import java.util.UUID;
 
 public abstract class BaseCycloneDxMojo extends AbstractMojo {
+    static final String CYCLONEDX_PLUGIN_KEY = "org.cyclonedx:cyclonedx-maven-plugin";
+    static final String PROJECT_TYPE = "projectType";
 
     @Parameter(property = "project", readonly = true, required = true)
     private MavenProject project;
@@ -72,7 +74,7 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
      *
      * @since 2.0.0
      */
-    @Parameter(property = "projectType", defaultValue = "library", required = false)
+    @Parameter(property = PROJECT_TYPE, defaultValue = "library", required = false)
     private String projectType;
 
     /**
@@ -622,13 +624,8 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
             for (final PluginExecution execution : plugin.getExecutions()) {
                 if (execution.getGoals().contains("deploy")) {
                     final Xpp3Dom executionConf = (Xpp3Dom) execution.getConfiguration();
-                    boolean skipValue = defaultSkipValue;
-                    if (executionConf != null) {
-                        Xpp3Dom target = executionConf.getChild(parameter);
-                        if (target != null) {
-                            skipValue = Boolean.parseBoolean(target.getValue());
-                        }
-                    }
+                    final Xpp3Dom target = (executionConf == null) ? null : executionConf.getChild(parameter);
+                    final boolean skipValue = (target == null) ? defaultSkipValue : Boolean.parseBoolean(target.getValue());
                     if (!skipValue) {
                         return true;
                     }
diff --git a/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java b/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java
index 416596a..8573c33 100644
--- a/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java
+++ b/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java
@@ -27,6 +27,7 @@ import org.cyclonedx.maven.ProjectDependenciesConverter.BomDependencies;
 import org.cyclonedx.model.Component;
 import org.cyclonedx.model.Dependency;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -117,9 +118,10 @@ public class CycloneDxAggregateMojo extends CycloneDxMojo {
         // root project: analyze and aggregate all the modules
         getLog().info((reactorProjects.size() <= 1) ? MESSAGE_RESOLVING_DEPS : MESSAGE_RESOLVING_AGGREGATED_DEPS);
 
+        final List<String> excludedProjects = new ArrayList<>();
         for (final MavenProject mavenProject : reactorProjects) {
             if (shouldExclude(mavenProject)) {
-                getLog().info("Excluding " + mavenProject.getArtifactId());
+            	excludedProjects.add(mavenProject.getArtifactId());
                 continue;
             }
 
@@ -135,6 +137,8 @@ public class CycloneDxAggregateMojo extends CycloneDxMojo {
             projectDependencies.forEach(dependencies::putIfAbsent);
         }
 
+        excludedProjects.stream().sorted(String.CASE_INSENSITIVE_ORDER).forEach(excluded -> getLog().info("Excluding " + excluded));
+
         addMavenProjectsAsParentDependencies(reactorProjects, dependencies);
 
         return "makeAggregateBom";
diff --git a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java
index a3b2220..ca659c4 100644
--- a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java
+++ b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java
@@ -60,6 +60,8 @@ import java.util.List;
 import java.util.Properties;
 import java.util.TreeMap;
 import java.util.stream.Collectors;
+import org.apache.maven.model.Plugin;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
 
 @Singleton
 @Named
@@ -162,6 +164,7 @@ public class DefaultModelConverter implements ModelConverter {
         component.setName(artifact.getArtifactId());
         component.setVersion(artifact.getBaseVersion());
         component.setType(Component.Type.LIBRARY);
+         
         try {
             logger.debug(BaseCycloneDxMojo.MESSAGE_CALCULATING_HASHES);
             component.setHashes(BomUtils.calculateHashes(artifact.getFile(), schemaVersion));
@@ -177,7 +180,12 @@ public class DefaultModelConverter implements ModelConverter {
         }
         try {
             final MavenProject project = getEffectiveMavenProject(artifact);
+            
             if (project != null) {
+                String projectType = getPluginConfiguration(project, BaseCycloneDxMojo.PROJECT_TYPE);
+                if (projectType != null) {
+                    component.setType(resolveProjectType(projectType));
+                }
                 extractComponentMetadata(project, component, schemaVersion, includeLicenseText);
             }
         } catch (ProjectBuildingException e) {
@@ -191,6 +199,13 @@ public class DefaultModelConverter implements ModelConverter {
 
     }
 
+    public String getPluginConfiguration(MavenProject project, String property) {
+        Plugin plugin = project.getPlugin(BaseCycloneDxMojo.CYCLONEDX_PLUGIN_KEY);
+        Xpp3Dom configuration = (plugin == null) ? null : (Xpp3Dom) plugin.getConfiguration();
+        Xpp3Dom value = (configuration == null) ? null : configuration.getChild(property);
+        return (value == null) ? null : value.getValue();
+    }
+
     private static void setExternalReferences(Component component, ExternalReference[] externalReferences) {
         if (externalReferences == null || externalReferences.length == 0) {
             return;
@@ -222,8 +237,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));
             }
         }
@@ -272,7 +287,7 @@ public class DefaultModelConverter implements ModelConverter {
     }
 
     private void addExternalReference(final ExternalReference.Type referenceType, final String url, final Component component) {
-        if (isURLBlank(url) || doesComponentHaveExternalReference(component, referenceType)) {
+        if (isBlank(url) || doesComponentHaveExternalReference(component, referenceType)) {
             return;
         }
         try {
@@ -328,8 +343,7 @@ public class DefaultModelConverter implements ModelConverter {
         return licenseChoice;
     }
 
-    private boolean resolveLicenseInfo(final LicenseChoice licenseChoice, final LicenseChoice licenseChoiceToResolve, final Version schemaVersion)
-    {
+    private boolean resolveLicenseInfo(final LicenseChoice licenseChoice, final LicenseChoice licenseChoiceToResolve, final Version schemaVersion) {
         if (licenseChoiceToResolve != null) {
             if (licenseChoiceToResolve.getLicenses() != null && !licenseChoiceToResolve.getLicenses().isEmpty()) {
                 licenseChoice.addLicense(licenseChoiceToResolve.getLicenses().get(0));
@@ -422,7 +436,11 @@ public class DefaultModelConverter implements ModelConverter {
         return Component.Type.LIBRARY;
     }
 
-    private static boolean isURLBlank(String url) {
-        return url == null || url.isEmpty() || url.trim().length() == 0;
+    private static boolean isBlank(String s) {
+        return s == null || s.isEmpty() || s.trim().isEmpty();
+    }
+
+    private static boolean isLicenseBlank(org.apache.maven.model.License license) {
+        return isBlank(license.getName()) && isBlank(license.getUrl());
     }
 }
diff --git a/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java b/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java
index 3b00eda..b8a286d 100644
--- a/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java
+++ b/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java
@@ -40,10 +40,12 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.lang.reflect.Field;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -242,6 +244,7 @@ public class DefaultProjectDependenciesConverter implements ProjectDependenciesC
 
         // Check all BOM components have an associated BOM dependency
 
+        final List<String> notDepended = new ArrayList<>();
         for (Iterator<Map.Entry<String, Component>> it = components.entrySet().iterator(); it.hasNext(); ) {
             Map.Entry<String, Component> entry = it.next();
             if (!dependencies.containsKey(entry.getKey())) {
@@ -250,10 +253,12 @@ public class DefaultProjectDependenciesConverter implements ProjectDependenciesC
                 }
                 it.remove();
             } else if (!dependsOns.contains(entry.getKey())) {
-                logger.warn("BOM dependency listed but is not depended upon: " + entry.getKey());
+                notDepended.add(entry.getKey());
             }
         }
 
+        notDepended.stream().sorted(String.CASE_INSENSITIVE_ORDER).forEach(dependency -> logger.warn("BOM dependency listed but is not depended upon: " + dependency));
+
         // include BOM main component
         Component main = metadata.getComponent();
         final String mainBomRef = main.getBomRef();


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/java/org/cyclonedx/maven/Issue521Test.java b/src/test/java/org/cyclonedx/maven/Issue521Test.java
new file mode 100644
index 0000000..5dae56c
--- /dev/null
+++ b/src/test/java/org/cyclonedx/maven/Issue521Test.java
@@ -0,0 +1,77 @@
+package org.cyclonedx.maven;
+
+import org.junit.runner.RunWith;
+
+
+import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder;
+import io.takari.maven.testing.executor.MavenVersions;
+import io.takari.maven.testing.executor.junit.MavenJUnitTestRunner;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import static org.cyclonedx.maven.TestUtils.readXML;
+
+
+import java.io.File;
+import java.io.IOException;
+
+
+
+import org.junit.Test;
+
+@RunWith(MavenJUnitTestRunner.class)
+@MavenVersions({"3.6.3"})
+public class Issue521Test extends BaseMavenVerifier {
+    public Issue521Test(MavenRuntimeBuilder runtimeBuilder) throws Exception {
+        super(runtimeBuilder);
+    }
+
+    @Test
+    public void testBomJsonContent() throws Exception {
+        File projDir = cleanAndBuild("issue-521", null);
+        checkBomXml(projDir, "target/bom.xml", "issue-521-app", "application");
+        checkBomJson(projDir, "target/bom.json", "issue-521-app", "application");
+    }
+
+    private static void assertFileExists(File file) {
+        assertTrue(file.exists(), String.format("File %s should exist", file));
+    }
+
+    private void checkBomJson(File basedir, String filePath, String expectedName, String expectedType) throws IOException {
+        File bomJsonFile = new File(basedir, filePath);
+        assertFileExists(bomJsonFile);
+
+        // Leggi il contenuto del file bom.json e verifica la presenza dei campi desiderati
+        String bomContents = fileRead(bomJsonFile, true);
+        assertTrue(bomContents.contains("\"name\" : \"" + expectedName + "\""), 
+            String.format("bom.json should contain a module with name '%s'", expectedName));
+        assertTrue(bomContents.contains("\"type\" : \"" + expectedType + "\""), 
+            String.format("bom.json should contain a module with type '%s'", expectedType));
+    }
+
+    private void checkBomXml(File basedir, String filePath, String expectedName, String expectedType) throws Exception {
+        File bomXmlFile = new File(basedir, filePath);
+        assertFileExists(bomXmlFile);
+
+        final Document bom = readXML(bomXmlFile);
+
+        final NodeList componentsList = bom.getElementsByTagName("component");
+        assertTrue(componentsList.getLength() > 0, "Expected at least one component element");
+
+        boolean found=false;
+        for (int i = 0; i < componentsList.getLength(); i++) {
+            Element component = (Element) componentsList.item(i);
+            String name = component.getElementsByTagName("name").item(0).getTextContent();
+            String type = component.getAttribute("type");
+
+            if (expectedName.equals(name) && expectedType.equals(type)) {
+                found = true;
+                break;
+            }
+        }
+
+        assertTrue(found, "Expected to find a component with name 'module1' and type 'application'");
+    }
+}
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>
diff --git a/src/test/resources/issue-521/app/pom.xml b/src/test/resources/issue-521/app/pom.xml
new file mode 100644
index 0000000..381bfd6
--- /dev/null
+++ b/src/test/resources/issue-521/app/pom.xml
@@ -0,0 +1,29 @@
+<?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-521</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>issue-521-app</artifactId>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.cyclonedx</groupId>
+                <artifactId>cyclonedx-maven-plugin</artifactId>
+                <version>${current.version}</version>
+                <configuration>
+                    <projectType>application</projectType>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/src/test/resources/issue-521/lib/pom.xml b/src/test/resources/issue-521/lib/pom.xml
new file mode 100644
index 0000000..604d5e6
--- /dev/null
+++ b/src/test/resources/issue-521/lib/pom.xml
@@ -0,0 +1,15 @@
+<?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-521</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>issue-521-lib</artifactId>
+</project>
diff --git a/src/test/resources/issue-521/pom.xml b/src/test/resources/issue-521/pom.xml
new file mode 100644
index 0000000..0cfbe81
--- /dev/null
+++ b/src/test/resources/issue-521/pom.xml
@@ -0,0 +1,51 @@
+<?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-521</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>app</module>
+        <module>lib</module>
+    </modules>
+
+    <properties>
+        <revision>1.0.0</revision>
+        <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>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.cyclonedx</groupId>
+                <artifactId>cyclonedx-maven-plugin</artifactId>
+                <version>${current.version}</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>makeAggregateBom</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file