diff --git a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java index 4ce1eafe..21599b84 100644 --- a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java +++ b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java @@ -215,21 +215,11 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo { private String outputTimestamp; /** - * External references to be added. - *

- * They will be injected in two locations: - *

- *
    - *
  1. $.metadata.component.externalReferences[]
  2. - *
  3. $.components[].externalReferences[] (only for $.components[] provided by the project)
  4. - *
+ * External references to be added to $.metadata.component.externalReferences[]. */ @Parameter private ExternalReference[] externalReferences; - @Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true) - private MojoExecution execution; - @org.apache.maven.plugins.annotations.Component private MavenProjectHelper mavenProjectHelper; @@ -270,7 +260,7 @@ protected String generatePackageUrl(final Artifact artifact) { } protected Component convert(Artifact artifact) { - return modelConverter.convert(execution, artifact, schemaVersion(), includeLicenseText); + return modelConverter.convert(artifact, schemaVersion(), includeLicenseText, externalReferences); } /** @@ -314,7 +304,7 @@ public void execute() throws MojoExecutionException { String analysis = extractComponentsAndDependencies(topLevelComponents, componentMap, dependencyMap); if (analysis != null) { - final Metadata metadata = modelConverter.convert(project, projectType, execution, schemaVersion(), includeLicenseText); + final Metadata metadata = modelConverter.convert(project, projectType, schemaVersion(), includeLicenseText, externalReferences); if (schemaVersion().getVersion() >= 1.3) { metadata.addProperty(newProperty("maven.goal", analysis)); diff --git a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java index 62b9dc12..fce6d4b6 100644 --- a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java +++ b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java @@ -18,42 +18,20 @@ */ package org.cyclonedx.maven; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; -import java.util.TreeMap; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.github.packageurl.MalformedPackageURLException; +import com.github.packageurl.PackageURL; import org.apache.commons.lang3.StringUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.artifact.handler.DefaultArtifactHandler; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.MailingList; -import org.apache.maven.model.Plugin; import org.apache.maven.model.building.ModelBuildingRequest; -import org.apache.maven.plugin.MojoExecution; import org.apache.maven.project.MavenProject; import org.apache.maven.project.ProjectBuilder; import org.apache.maven.project.ProjectBuildingException; import org.apache.maven.project.ProjectBuildingResult; import org.apache.maven.repository.RepositorySystem; -import org.codehaus.plexus.util.xml.Xpp3Dom; import org.cyclonedx.CycloneDxSchema; import org.cyclonedx.model.Component; import org.cyclonedx.model.ExternalReference; @@ -67,8 +45,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.github.packageurl.MalformedPackageURLException; -import com.github.packageurl.PackageURL; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import java.util.TreeMap; +import java.util.stream.Collectors; @Singleton @Named @@ -165,9 +153,7 @@ private String generatePackageUrl(String groupId, String artifactId, String vers } @Override - public Component convert(MojoExecution execution, Artifact artifact, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText) { - - // Populate basic fields from the `Artifact` instance + public Component convert(Artifact artifact, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText, ExternalReference[] externalReferences) { final Component component = new Component(); component.setGroup(artifact.getGroupId()); component.setName(artifact.getArtifactId()); @@ -202,8 +188,7 @@ public Component convert(MojoExecution execution, Artifact artifact, CycloneDxSc if (project != null) { // Populate external references - List externalReferences = extractExternalReferences(project, execution); - component.setExternalReferences(externalReferences); + setExternalReferences(component, externalReferences); // Extract the rest of the metadata for JARs, i.e., *described* artifacts if (isDescribedArtifact(artifact)) { @@ -217,68 +202,13 @@ public Component convert(MojoExecution execution, Artifact artifact, CycloneDxSc } - private List extractExternalReferences(MavenProject project, MojoExecution activeExecution) { - Plugin activePlugin = activeExecution.getPlugin(); - return project - .getBuild() - .getPlugins() - .stream() - .filter(plugin -> activePlugin.getGroupId().equals(plugin.getGroupId()) && activePlugin.getArtifactId().equals(plugin.getArtifactId())) - .findFirst() - .map(plugin -> extractExternalReferences(plugin, activeExecution)) - .orElseGet(ArrayList::new); - } - - private static List extractExternalReferences(Plugin plugin, MojoExecution activeExecution) { - - // Collect external references from the execution configuration - List executionExternalReferences = plugin - .getExecutions() - .stream() - .filter(execution -> activeExecution.getExecutionId().equals(execution.getId())) - .flatMap(execution -> { - Xpp3Dom executionConfig = (Xpp3Dom) execution.getConfiguration(); - return ExternalReferenceConfigDto.parseDom(executionConfig).stream(); - }) - .collect(Collectors.toList()); - - // Collect external references from the plugin configuration - Xpp3Dom pluginConfig = (Xpp3Dom) plugin.getConfiguration(); - List pluginExternalReferences = ExternalReferenceConfigDto.parseDom(pluginConfig); - - // Combine collected external references - return Stream - .concat(executionExternalReferences.stream(), pluginExternalReferences.stream()) - .distinct() - .collect(Collectors.toList()); - - } - - private static final class ExternalReferenceConfigDto { - - private static final XmlMapper MAPPER = XmlMapper - .builder() - .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) - .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS) - .build(); - - private static List parseDom(@Nullable Xpp3Dom dom) { - if (dom == null) { - return new ArrayList<>(); - } - String xml = dom.toString(); - try { - ExternalReferenceConfigDto dto = MAPPER.readValue(xml, ExternalReferenceConfigDto.class); - @Nullable List externalReferences = dto.externalReferences; - return externalReferences != null ? externalReferences : new ArrayList<>(); - } catch (JsonProcessingException error) { - throw new RuntimeException(error); - } + private static void setExternalReferences(Component component, ExternalReference[] externalReferences) { + if (externalReferences == null || externalReferences.length == 0) { + return; } - - @JsonProperty - private List externalReferences; - + // We need a mutable `List`, hence `Arrays.asList()` won't work! + List externalReferences_ = Arrays.stream(externalReferences).collect(Collectors.toList()); + component.setExternalReferences(externalReferences_); } private boolean isModified(Artifact artifact) { @@ -433,7 +363,7 @@ else if (licenseChoiceToResolve.getExpression() != null && CycloneDxSchema.Versi } @Override - public Metadata convert(final MavenProject project, String projectType, MojoExecution execution, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText) { + public Metadata convert(final MavenProject project, String projectType, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText, ExternalReference[] externalReferences) { final Tool tool = new Tool(); final Properties properties = readPluginProperties(); tool.setVendor(properties.getProperty("vendor")); @@ -459,9 +389,7 @@ public Metadata convert(final MavenProject project, String projectType, MojoExec component.setType(resolveProjectType(projectType)); component.setPurl(generatePackageUrl(project.getArtifact())); component.setBomRef(component.getPurl()); - - List externalReferences = extractExternalReferences(project, execution); - component.setExternalReferences(externalReferences); + setExternalReferences(component, externalReferences); extractComponentMetadata(project, component, schemaVersion, includeLicenseText); final Metadata metadata = new Metadata(); diff --git a/src/main/java/org/cyclonedx/maven/ModelConverter.java b/src/main/java/org/cyclonedx/maven/ModelConverter.java index d3ae77f8..43115c8a 100644 --- a/src/main/java/org/cyclonedx/maven/ModelConverter.java +++ b/src/main/java/org/cyclonedx/maven/ModelConverter.java @@ -19,10 +19,10 @@ package org.cyclonedx.maven; import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.MojoExecution; import org.apache.maven.project.MavenProject; import org.cyclonedx.CycloneDxSchema; import org.cyclonedx.model.Component; +import org.cyclonedx.model.ExternalReference; import org.cyclonedx.model.Metadata; /** @@ -44,24 +44,23 @@ public interface ModelConverter { * Converts a Maven artifact (dependency or transitive dependency) into a * CycloneDX component. * - * @param execution the associated execution * @param artifact the artifact to convert * @param schemaVersion the target CycloneDX schema version * @param includeLicenseText should license text be included in bom? + * @param externalReferences the external references * @return a CycloneDX component */ - Component convert(MojoExecution execution, Artifact artifact, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText); + Component convert(Artifact artifact, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText, ExternalReference[] externalReferences); /** * Converts a MavenProject into a Metadata object. * * @param project the MavenProject to convert * @param projectType the target CycloneDX component type - * @param execution the associated execution * @param schemaVersion the target CycloneDX schema version * @param includeLicenseText should license text be included in bom? + * @param externalReferences the external references * @return a CycloneDX Metadata object */ - Metadata convert(MavenProject project, String projectType, MojoExecution execution, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText); - + Metadata convert(MavenProject project, String projectType, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText, ExternalReference[] externalReferences); } diff --git a/src/test/java/org/cyclonedx/maven/ExternalReferenceTest.java b/src/test/java/org/cyclonedx/maven/ExternalReferenceTest.java index 2d082b72..db8b80ee 100644 --- a/src/test/java/org/cyclonedx/maven/ExternalReferenceTest.java +++ b/src/test/java/org/cyclonedx/maven/ExternalReferenceTest.java @@ -34,7 +34,6 @@ public void testAddedExternalReferences() throws Exception { verifier .forProject(projDir) .withCliOption("-Dcyclonedx-maven-plugin.version=" + getCurrentVersion()) - .withCliOption("-X") .withCliOption("-B") .execute("clean", "verify") .assertErrorFreeLog(); @@ -45,12 +44,6 @@ public void testAddedExternalReferences() throws Exception { "$.metadata.component.externalReferences[?(@.type=='chat')].url", Collections.singleton("https://acme.com/parent")); - // Verify parent components - assertExternalReferences( - new File(projDir, "target/bom.json"), - "$.components[?(@.name=='child')].externalReferences[?(@.type=='chat')].url", - Arrays.asList("https://acme.com/parent", "https://acme.com/child")); - // Verify child metadata assertExternalReferences( new File(projDir, "child/target/bom.json"),