Skip to content

Commit

Permalink
add CycloneDxAggregateEnforceMojo
Browse files Browse the repository at this point in the history
Signed-off-by: XenoAmess <xenoamess@gmail.com>
  • Loading branch information
XenoAmess committed May 7, 2022
1 parent 8b3ae0c commit ec53236
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 10 deletions.
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@
<scope>test</scope>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.spdx</groupId>
<artifactId>java-spdx-library</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<scope>provided</scope>
<version>23.0.0</version>
</dependency>
</dependencies>

<prerequisites>
Expand Down
55 changes: 53 additions & 2 deletions src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.commons.io.input.BOMInputStream;
import org.jetbrains.annotations.NotNull;

import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;

Expand Down Expand Up @@ -170,6 +171,21 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo implements Contextu
@Parameter(property = "cyclonedx.verbose", defaultValue = "true", required = false)
private boolean verbose = true;

@Parameter(property = "enforceExcludeArtifactId", required = false)
protected String[] enforceExcludeArtifactId;

@Parameter(property = "enforceComponentsSameVersion", defaultValue = "true", required = false)
protected boolean enforceComponentsSameVersion = true;

@Parameter(property = "enforceLicensesBlackList", required = false)
protected String[] enforceLicensesBlackList;

@Parameter(property = "enforceLicensesWhiteList", required = false)
protected String[] enforceLicensesWhiteList;

@Parameter(property = "mergeBomFile", required = false)
protected File mergeBomFile;

/**
* Various messages sent to console.
*/
Expand Down Expand Up @@ -764,7 +780,7 @@ private MavenProject readPom(InputStream in) {
protected void execute(Set<Component> components, Set<Dependency> dependencies, MavenProject mavenProject) throws MojoExecutionException {
try {
getLog().info(MESSAGE_CREATING_BOM);
final Bom bom = new Bom();
Bom bom = new Bom();
if (schemaVersion().getVersion() >= 1.1 && includeBomSerialNumber) {
bom.setSerialNumber("urn:uuid:" + UUID.randomUUID());
}
Expand Down Expand Up @@ -794,13 +810,48 @@ protected void execute(Set<Component> components, Set<Dependency> dependencies,
return;
}

bom = postProcessingBom(bom);

createBom(bom, mavenProject);

} catch (GeneratorException | ParserConfigurationException | IOException e) {
} catch (Exception e) {
throw new MojoExecutionException("An error occurred executing " + this.getClass().getName() + ": " + e.getMessage(), e);
}
}

@NotNull
protected Bom postProcessingBom(@NotNull Bom bom) throws Exception {
if (mergeBomFile != null) {
Bom mergeBom;
try {
mergeBom = new JsonParser().parse(mergeBomFile);
} catch (Exception e) {
mergeBom = new XmlParser().parse(mergeBomFile);
}
{
LinkedHashSet<Component> components = new LinkedHashSet<>();
if (mergeBom.getComponents() != null) {
components.addAll(mergeBom.getComponents());
}
if (bom.getComponents() != null) {
components.addAll(bom.getComponents());
}
bom.setComponents(new ArrayList<>(components));
}
{
LinkedHashSet<Dependency> dependencies = new LinkedHashSet<>();
if (mergeBom.getDependencies() != null) {
dependencies.addAll(mergeBom.getDependencies());
}
if (bom.getDependencies() != null) {
dependencies.addAll(bom.getDependencies());
}
bom.setDependencies(new ArrayList<>(dependencies));
}
}
return bom;
}

private void createBom(Bom bom, MavenProject mavenProject) throws ParserConfigurationException, IOException, GeneratorException,
MojoExecutionException {
if (outputFormat.trim().equalsIgnoreCase("all") || outputFormat.trim().equalsIgnoreCase("xml")) {
Expand Down
206 changes: 206 additions & 0 deletions src/main/java/org/cyclonedx/maven/CycloneDxAggregateEnforceMojo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* This file is part of CycloneDX Maven Plugin.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) OWASP Foundation. All Rights Reserved.
*/
package org.cyclonedx.maven;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.cyclonedx.maven.utils.SpdxLicenseUtil;
import org.cyclonedx.model.Bom;
import org.cyclonedx.model.Component;
import org.cyclonedx.model.License;
import org.cyclonedx.model.LicenseChoice;
import org.jetbrains.annotations.NotNull;
import org.spdx.library.InvalidSPDXAnalysisException;
import org.spdx.library.model.license.AnyLicenseInfo;
import org.spdx.library.model.license.LicenseInfoFactory;

@Mojo(
name = "enforceAggregateBom",
defaultPhase = LifecyclePhase.PACKAGE,
aggregator = true,
requiresOnline = true,
requiresDependencyCollection = ResolutionScope.TEST,
requiresDependencyResolution = ResolutionScope.TEST
)
public class CycloneDxAggregateEnforceMojo extends CycloneDxAggregateMojo {

@Override
protected boolean shouldExclude(@NotNull String mavenProjectArtifactId) {
if (super.shouldExclude(mavenProjectArtifactId)) {
return true;
}
if (enforceExcludeArtifactId != null && enforceExcludeArtifactId.length > 0) {
if (Arrays.asList(enforceExcludeArtifactId).contains(mavenProjectArtifactId)) {
return true;
}
}
return false;
}

@Override
@NotNull
protected Bom postProcessingBom(@NotNull Bom bom) throws Exception {
bom = super.postProcessingBom(bom);
doEnforceComponentsSameVersion(bom);
doEnforceLicensesBlackListAndWhiteList(bom);
return bom;
}

private void doEnforceComponentsSameVersion(@NotNull Bom bom) throws MojoFailureException {
if (this.enforceComponentsSameVersion) {
List<Component> components = bom.getComponents();
if (components != null) {
Map<Pair<String, String>, Set<String>> componentMap =
new HashMap<>((int) Math.ceil(components.size() / 0.75));
for (Component component : components) {
if (component == null) {
continue;
}
String group = component.getGroup();
String name = component.getName();
String version = component.getVersion();
Pair<String, String> key = Pair.of(group, name);
Set<String> versions = componentMap.computeIfAbsent(
key,
stringStringPair -> new HashSet<>()
);
versions.add(version);
}
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<Pair<String, String>, Set<String>> entry : componentMap.entrySet()) {
Pair<String, String> key = entry.getKey();
Set<String> versions = entry.getValue();
if (versions.size() > 1) {
stringBuilder
.append("[ERROR]Duplicated versions for ")
.append(key.getLeft())
.append(":")
.append(key.getRight())
.append(" , versions : ")
.append(StringUtils.join(versions.iterator(), ","))
.append("\n");
}
}
if (stringBuilder.length() > 0) {
throw new MojoFailureException(stringBuilder.toString());
}
}
}
}

private void doEnforceLicensesBlackListAndWhiteList(@NotNull Bom bom) throws MojoFailureException {
List<Component> components = bom.getComponents();
if (components != null) {
StringBuilder stringBuilder = new StringBuilder();
for (Component component : components) {
if (component == null) {
continue;
}
String group = component.getGroup();
String name = component.getName();
LicenseChoice licenseChoice = component.getLicenseChoice();
if (licenseChoice == null) {
continue;
}
if (StringUtils.isNotBlank(licenseChoice.getExpression())) {
try {
AnyLicenseInfo anyLicenseInfo = LicenseInfoFactory.parseSPDXLicenseString(licenseChoice.getExpression());
if (!ArrayUtils.isEmpty(this.enforceLicensesBlackList)) {
if (!SpdxLicenseUtil.isLicensePassBlackList(anyLicenseInfo, this.enforceLicensesBlackList)) {
stringBuilder
.append("[ERROR]License in blackList for ")
.append(group)
.append(":")
.append(name)
.append(" , license : ")
.append(licenseChoice.getExpression())
.append("\n");
}
}
if (!ArrayUtils.isEmpty(this.enforceLicensesWhiteList)) {
if (!SpdxLicenseUtil.isLicensePassWhiteList(anyLicenseInfo, this.enforceLicensesWhiteList)) {
stringBuilder
.append("[ERROR]License not in whiteList for ")
.append(group)
.append(":")
.append(name)
.append(" , license : ")
.append(licenseChoice.getExpression())
.append("\n");
}
}
} catch (InvalidSPDXAnalysisException e) {
getLog().warn(e);
}
} else if (licenseChoice.getLicenses() != null) {
for (License license : licenseChoice.getLicenses()) {
if (!ArrayUtils.isEmpty(this.enforceLicensesBlackList)) {
if (
ArrayUtils.contains(this.enforceLicensesBlackList, license.getId())
|| ArrayUtils.contains(this.enforceLicensesBlackList, license.getName())
) {
stringBuilder
.append("[ERROR]License in blackList for ")
.append(group)
.append(":")
.append(name)
.append(" , license : ")
.append(license.getId())
.append("\n");
}
}
if (!ArrayUtils.isEmpty(this.enforceLicensesWhiteList)) {
if (
!(
ArrayUtils.contains(this.enforceLicensesBlackList, license.getId())
|| ArrayUtils.contains(this.enforceLicensesBlackList, license.getName())
)
) {
stringBuilder
.append("[ERROR]License not in whiteList for ")
.append(group)
.append(":")
.append(name)
.append(" , license : ")
.append(license.getId())
.append("\n");
}
}
}
}
}
if (stringBuilder.length() > 0) {
throw new MojoFailureException(stringBuilder.toString());
}
}
}

}
19 changes: 11 additions & 8 deletions src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Execute;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalysis;
import org.cyclonedx.model.Component;
import org.cyclonedx.model.Dependency;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.LinkedHashMap;
Expand All @@ -45,17 +45,20 @@
)
public class CycloneDxAggregateMojo extends BaseCycloneDxMojo {

protected boolean shouldExclude(MavenProject mavenProject) {
boolean shouldExclude = false;
protected boolean shouldExclude(@NotNull MavenProject mavenProject) {
return this.shouldExclude(mavenProject.getArtifactId());
}

protected boolean shouldExclude(@NotNull String mavenProjectArtifactId) {
if (excludeArtifactId != null && excludeArtifactId.length > 0) {
shouldExclude = Arrays.asList(excludeArtifactId).contains(mavenProject.getArtifactId());
}
if (excludeTestProject && mavenProject.getArtifactId().contains("test")) {
shouldExclude = true;
if (Arrays.asList(excludeArtifactId).contains(mavenProjectArtifactId)) {
return true;
}
}
return shouldExclude;
return excludeTestProject && mavenProjectArtifactId.contains("test");
}

@Override
public void execute() throws MojoExecutionException {
final boolean shouldSkip = Boolean.parseBoolean(System.getProperty("cyclonedx.skip", Boolean.toString(getSkip())));
if (shouldSkip) {
Expand Down
Loading

0 comments on commit ec53236

Please sign in to comment.