From 6b7db61137d233e92fdbaf0e7b13b683e679c738 Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Thu, 14 Dec 2023 20:35:17 +0100 Subject: [PATCH] Add p2-aware model converter for CycloneDX SBOM generation --- tycho-extras/pom.xml | 1 + .../.settings/org.eclipse.jdt.core.prefs | 8 ++ tycho-extras/tycho-sbom/pom.xml | 55 ++++++++ .../eclipse/tycho/sbom/P2ModelConverter.java | 127 ++++++++++++++++++ .../resources/META-INF/plexus/components.xml | 9 ++ .../tycho/sbom/P2ModelConverterTest.java | 55 ++++++++ 6 files changed, 255 insertions(+) create mode 100644 tycho-extras/tycho-sbom/.settings/org.eclipse.jdt.core.prefs create mode 100644 tycho-extras/tycho-sbom/pom.xml create mode 100644 tycho-extras/tycho-sbom/src/main/java/org/eclipse/tycho/sbom/P2ModelConverter.java create mode 100644 tycho-extras/tycho-sbom/src/main/resources/META-INF/plexus/components.xml create mode 100644 tycho-extras/tycho-sbom/src/test/java/org/eclipse/tycho/sbom/P2ModelConverterTest.java diff --git a/tycho-extras/pom.xml b/tycho-extras/pom.xml index 0123fcf5e4..e258f59607 100644 --- a/tycho-extras/pom.xml +++ b/tycho-extras/pom.xml @@ -42,6 +42,7 @@ target-platform-validation-plugin tycho-pomless tycho-dependency-tools-plugin + tycho-sbom diff --git a/tycho-extras/tycho-sbom/.settings/org.eclipse.jdt.core.prefs b/tycho-extras/tycho-sbom/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..cf2cd4590a --- /dev/null +++ b/tycho-extras/tycho-sbom/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=17 diff --git a/tycho-extras/tycho-sbom/pom.xml b/tycho-extras/tycho-sbom/pom.xml new file mode 100644 index 0000000000..caac2ab4de --- /dev/null +++ b/tycho-extras/tycho-sbom/pom.xml @@ -0,0 +1,55 @@ + + 4.0.0 + + org.eclipse.tycho.extras + tycho-extras + 5.0.0-SNAPSHOT + + tycho-sbom + Tycho SBOM model extension + + + + org.eclipse.tycho + tycho-core + + + org.eclipse.tycho + tycho-api + ${project.version} + + + org.eclipse.tycho + p2-maven-plugin + ${project.version} + maven-plugin + + + org.eclipse.tycho + tycho-versions-plugin + ${project.version} + maven-plugin + + + + org.apache.maven + maven-plugin-api + + + org.codehaus.plexus + plexus-component-annotations + + + + org.cyclonedx + cyclonedx-maven-plugin + 2.7.10 + + + + junit + junit + test + + + \ No newline at end of file diff --git a/tycho-extras/tycho-sbom/src/main/java/org/eclipse/tycho/sbom/P2ModelConverter.java b/tycho-extras/tycho-sbom/src/main/java/org/eclipse/tycho/sbom/P2ModelConverter.java new file mode 100644 index 0000000000..ba31336985 --- /dev/null +++ b/tycho-extras/tycho-sbom/src/main/java/org/eclipse/tycho/sbom/P2ModelConverter.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2023 Patrick Ziegler and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.sbom; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Set; + +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.Artifact; +import org.codehaus.plexus.component.annotations.Component; +import org.cyclonedx.maven.DefaultModelConverter; +import org.cyclonedx.maven.ModelConverter; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.tycho.ArtifactKey; +import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.DefaultArtifactKey; +import org.eclipse.tycho.core.resolver.target.ArtifactTypeHelper; +import org.eclipse.tycho.versions.engine.Versions; + +/** + * Custom implementation of the CycloneDX model converter with support for both + * Maven and p2 artifacts. The generated PURL is usually of the form: + * + *
+ * pkg:/p2/<id>@<version>?classifier=<classifier>&location=<download-url>
+ * 
+ * + * This converter can be used with the {@code cyclonedx-maven-plugin} by adding + * it as a dependency as follows: + * + *
+ * <plugin>
+ *   <groupId>org.cyclonedx</groupId>
+ *   <artifactId>cyclonedx-maven-plugin</artifactId>
+ *   <dependencies>
+ *     <dependency>
+ *       <groupId>org.eclipse.tycho.extras</groupId>
+ *       <artifactId>tycho-sbom</artifactId>
+ *     </dependency>
+ *   </dependencies>
+ * </plugin>
+ * 
+ */ +@Component(role = ModelConverter.class, hint = "p2") +public class P2ModelConverter extends DefaultModelConverter { + private static final Set SUPPORTED_TYPES = Set.of(ArtifactType.TYPE_BUNDLE_FRAGMENT, + ArtifactType.TYPE_ECLIPSE_PLUGIN, ArtifactType.TYPE_ECLIPSE_FEATURE); + + @Override + public String generatePackageUrl(Artifact artifact) { + if (SUPPORTED_TYPES.contains(artifact.getType())) { + return generateP2PackageUrl(artifact, true, true); + } + return super.generatePackageUrl(artifact); + } + + @Override + public String generatePackageUrl(org.eclipse.aether.artifact.Artifact artifact) { + return generatePackageUrl(RepositoryUtils.toArtifact(artifact)); + } + + @Override + public String generateVersionlessPackageUrl(Artifact artifact) { + if (SUPPORTED_TYPES.contains(artifact.getType())) { + return generateP2PackageUrl(artifact, false, true); + } + return super.generateVersionlessPackageUrl(artifact); + } + + @Override + public String generateVersionlessPackageUrl(org.eclipse.aether.artifact.Artifact artifact) { + return generateVersionlessPackageUrl(RepositoryUtils.toArtifact(artifact)); + } + + @Override + public String generateClassifierlessPackageUrl(org.eclipse.aether.artifact.Artifact artifact) { + Artifact mavenArtifact = RepositoryUtils.toArtifact(artifact); + if (SUPPORTED_TYPES.contains(mavenArtifact.getType())) { + return generateP2PackageUrl(mavenArtifact, true, false); + } + return super.generateClassifierlessPackageUrl(artifact); + } + + private String generateP2PackageUrl(Artifact artifact, boolean withVersion, boolean withClassifier) { + // TODO Resolve "artifact" and use e.g. TychoProjectManager w/ proper qualifier + ArtifactKey artifactKey = new DefaultArtifactKey(artifact.getType(), artifact.getArtifactId(), + Versions.toCanonicalVersion(artifact.getVersion())); + IArtifactKey p2artifactKey = ArtifactTypeHelper.toP2ArtifactKey(artifactKey); + // TODO Try to find p2 location + String location = "unknown"; + if (artifact.getFile() != null) { + location = artifact.getFile().toURI().toString(); + } + if (artifact.getDownloadUrl() != null) { + location = artifact.getDownloadUrl(); + } + String encodedLocation = URLEncoder.encode(location, StandardCharsets.UTF_8); + // + StringBuilder builder = new StringBuilder(); + builder.append("pkg:p2/"); + builder.append(p2artifactKey.getId()); + if (withVersion) { + builder.append('@'); + builder.append(p2artifactKey.getVersion()); + } + builder.append('?'); + if (withClassifier) { + builder.append("classifier="); + builder.append(p2artifactKey.getClassifier()); + builder.append('&'); + } + builder.append("location="); + builder.append(encodedLocation); + return builder.toString(); + } +} diff --git a/tycho-extras/tycho-sbom/src/main/resources/META-INF/plexus/components.xml b/tycho-extras/tycho-sbom/src/main/resources/META-INF/plexus/components.xml new file mode 100644 index 0000000000..9e4c319a81 --- /dev/null +++ b/tycho-extras/tycho-sbom/src/main/resources/META-INF/plexus/components.xml @@ -0,0 +1,9 @@ + + + + org.cyclonedx.maven.ModelConverter + p2 + org.eclipse.tycho.sbom.P2ModelConverter + + + \ No newline at end of file diff --git a/tycho-extras/tycho-sbom/src/test/java/org/eclipse/tycho/sbom/P2ModelConverterTest.java b/tycho-extras/tycho-sbom/src/test/java/org/eclipse/tycho/sbom/P2ModelConverterTest.java new file mode 100644 index 0000000000..540fda3255 --- /dev/null +++ b/tycho-extras/tycho-sbom/src/test/java/org/eclipse/tycho/sbom/P2ModelConverterTest.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2023 Patrick Ziegler and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.sbom; + +import static org.junit.Assert.assertEquals; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; +import org.cyclonedx.maven.ModelConverter; +import org.eclipse.tycho.ArtifactType; +import org.eclipse.tycho.p2maven.repository.EclipsePluginArtifactHandler; +import org.junit.Before; +import org.junit.Test; + +public class P2ModelConverterTest { + private Artifact artifact; + private ModelConverter modelConverter; + + @Before + public void setUp() { + artifact = new DefaultArtifact("p2.eclipse.plugin", "org.eclipse.platform", "4.30.0.v20231201-0110", "compile", + ArtifactType.TYPE_ECLIPSE_PLUGIN, null, new EclipsePluginArtifactHandler()); + artifact.setDownloadUrl( + "https://download.eclipse.org/releases/2023-12/202312061001/plugins/org.eclipse.platform_4.30.0.v20231201-0110.jar"); + modelConverter = new P2ModelConverter(); + } + + @Test + public void testGeneratePackageUrl() { + String purl = modelConverter.generatePackageUrl(artifact); + String location = URLEncoder.encode(artifact.getDownloadUrl(), StandardCharsets.UTF_8); + assertEquals(purl, + "pkg:p2/org.eclipse.platform@4.30.0.v20231201-0110?classifier=osgi.bundle&location=" + location); + } + + @Test + public void testGeneratePackageUrlWithoutVersion() { + String purl = modelConverter.generateVersionlessPackageUrl(artifact); + String location = URLEncoder.encode(artifact.getDownloadUrl(), StandardCharsets.UTF_8); + assertEquals(purl, "pkg:p2/org.eclipse.platform?classifier=osgi.bundle&location=" + location); + } +}