Skip to content

Commit

Permalink
Print Maven POM as CycloneDX BOM
Browse files Browse the repository at this point in the history
  • Loading branch information
jkschneider committed Sep 19, 2020
1 parent b3784f3 commit 94995c3
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ private List<MavenModel> load(Collection<InputStreamModelSource> modelSources) {
m.getModuleVersion(),
m.getDependencyManagement(),
m.getDependencies(),
m.getLicenses(),
m.getTransitiveDependenciesByScope(),
m.getProperties(),
Stream.concat(
Expand Down Expand Up @@ -201,6 +202,9 @@ private MavenModel buildMavenModelRecursive(RepositorySystem repositorySystem,
),
dependencyManagement,
dependencies,
model.getLicenses().stream()
.map(l -> new MavenModel.License(l.getName()))
.collect(toList()),
transitiveDepenenciesByScope,
properties,
Stream.concat(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ public class MavenModel {
@With
List<Dependency> dependencies;

@ToString.Exclude
@With
List<License> licenses;

@With
Map<String, Set<ModuleVersionId>> transitiveDependenciesByScope;

Expand Down Expand Up @@ -96,6 +100,13 @@ public static class Dependency {
String scope;
}

@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@Data
public static class License {
@With
String name;
}

@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@Data
public static class ModuleVersionId implements Comparable<ModuleVersionId> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright 2020 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.maven.utilities;

import org.openrewrite.Tree;
import org.openrewrite.maven.AbstractMavenSourceVisitor;
import org.openrewrite.maven.tree.Maven;
import org.openrewrite.maven.tree.MavenModel;

import java.util.Map;
import java.util.Set;

public class PrintMavenAsCycloneDxBom extends AbstractMavenSourceVisitor<String> {
@Override
public String defaultTo(Tree t) {
return "";
}

@Override
public String visitPom(Maven.Pom pom) {
StringBuilder bom = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");

bom.append("<bom xmlns=\"http://cyclonedx.org/schema/bom/1.2\" serialNumber=\"urn:uuid:");
bom.append(pom.getId().toString());
bom.append("\" version=\"1\">\n");

bom.append(" <components>\n");
bom.append(" <component type=\"library\" bom-ref=\"").append(pom.getArtifactId()).append("\">\n");
bom.append(" <group>").append(pom.getGroupId()).append("</group>\n");
bom.append(" <name>").append(pom.getArtifactId()).append("</name>\n");
bom.append(" <version>").append(pom.getVersion()).append("</version>\n");
bom.append(" <purl>pkg:maven/").append(pom.getGroupId()).append("/").append(pom.getArtifactId())
.append("@").append(pom.getVersion()).append("</purl>\n");

writeLicenses(pom, bom);
writeDependencies(pom, bom);

bom.append(" </component>\n");
bom.append(" </components>\n");
bom.append("</bom>");

return bom.toString();
}

private void writeLicenses(Maven.Pom pom, StringBuilder bom) {
if (!pom.getModel().getLicenses().isEmpty()) {
bom.append(" <licenses>\n");

for (MavenModel.License license : pom.getModel().getLicenses()) {
bom.append(" <license>").append(license.getName()).append("</license>\n");
}

bom.append(" </licenses>\n");
}
}

private void writeDependencies(Maven.Pom pom, StringBuilder bom) {
if (!pom.getModel().getDependencies().isEmpty()) {
bom.append(" <dependencies>\n");
bom.append(" <dependency ref=\"").append(pom.getArtifactId()).append("\">\n");

for (Map.Entry<String, Set<MavenModel.ModuleVersionId>> dependenciesForScope : pom.getModel().getTransitiveDependenciesByScope().entrySet()) {
String scope = dependenciesForScope.getKey();
if (scope.equals("compile") || scope.equals("runtime")) {
for (MavenModel.ModuleVersionId mvid : dependenciesForScope.getValue()) {
bom.append(" <dependency ref=\"pkg:maven/")
.append(mvid.getGroupId())
.append("/")
.append(mvid.getArtifactId())
.append("@")
.append(mvid.getVersion())
.append("\"/>\n");
}
}
}

bom.append(" </dependency>\n");
bom.append(" </dependencies>\n");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2020 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.maven

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.openrewrite.maven.utilities.PrintMavenAsCycloneDxBom

class PrintMavenAsCycloneDxBomTest {
@Test
fun cycloneDxBom() {
val pom = MavenParser.builder()
.build()
.parse("""
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
""")

assertThat(PrintMavenAsCycloneDxBom().visitPom(pom[0])).isEqualTo("""
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" serialNumber="urn:uuid:${pom[0].id}" version="1">
<components>
<component type="library" bom-ref="my-app">
<group>com.mycompany.app</group>
<name>my-app</name>
<version>1</version>
<purl>pkg:maven/com.mycompany.app/my-app@1</purl>
<dependencies>
<dependency ref="my-app">
<dependency ref="pkg:maven/org.springframework/spring-beans@5.2.9.RELEASE"/>
<dependency ref="pkg:maven/org.springframework/spring-expression@5.2.9.RELEASE"/>
<dependency ref="pkg:maven/org.springframework.boot/spring-boot@2.3.4.RELEASE"/>
<dependency ref="pkg:maven/org.springframework.boot/spring-boot-actuator-autoconfigure@2.3.4.RELEASE"/>
<dependency ref="pkg:maven/org.springframework/spring-core@5.2.9.RELEASE"/>
<dependency ref="pkg:maven/org.springframework/spring-aop@5.2.9.RELEASE"/>
<dependency ref="pkg:maven/org.springframework.boot/spring-boot-autoconfigure@2.3.4.RELEASE"/>
<dependency ref="pkg:maven/org.springframework/spring-jcl@5.2.9.RELEASE"/>
<dependency ref="pkg:maven/org.springframework.boot/spring-boot-actuator@2.3.4.RELEASE"/>
<dependency ref="pkg:maven/org.springframework/spring-context@5.2.9.RELEASE"/>
</dependency>
</dependencies>
</component>
</components>
</bom>
""".trimIndent())
}
}

0 comments on commit 94995c3

Please sign in to comment.