From 7bbf61d3b6dd4f5e5e42498c8d1787559cee4e4e Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Thu, 26 Oct 2023 15:16:58 +0200 Subject: [PATCH] [409] Removes non-deployed artifacts from SBOM This PR detects reactor artifacts that are neither deployed by `maven-deploy-plugin` nor `nexus-staging-maven-plugin`. These artifacts will: * not appear as dependencies in the aggregate BOM, * not generate BOMs. Fixes #409. Signed-off-by: Piotr P. Karwasz --- src/it/makeAggregateBom/pom.xml | 1 + .../skipped/deploy-config/pom.xml | 43 ++++++++++++++ .../skipped/deploy-property/pom.xml | 34 +++++++++++ .../skipped/nexus-config/pom.xml | 48 +++++++++++++++ .../skipped/nexus-property/pom.xml | 45 ++++++++++++++ src/it/makeAggregateBom/skipped/pom.xml | 38 ++++++++++++ src/it/makeAggregateBom/verify.groovy | 29 ++++++--- .../cyclonedx/maven/BaseCycloneDxMojo.java | 59 ++++++++++++++++++- .../maven/CycloneDxAggregateMojo.java | 5 +- .../org/cyclonedx/maven/CycloneDxMojo.java | 6 ++ 10 files changed, 298 insertions(+), 10 deletions(-) create mode 100644 src/it/makeAggregateBom/skipped/deploy-config/pom.xml create mode 100644 src/it/makeAggregateBom/skipped/deploy-property/pom.xml create mode 100644 src/it/makeAggregateBom/skipped/nexus-config/pom.xml create mode 100644 src/it/makeAggregateBom/skipped/nexus-property/pom.xml create mode 100644 src/it/makeAggregateBom/skipped/pom.xml diff --git a/src/it/makeAggregateBom/pom.xml b/src/it/makeAggregateBom/pom.xml index 5d7ddabe..6a7742fa 100644 --- a/src/it/makeAggregateBom/pom.xml +++ b/src/it/makeAggregateBom/pom.xml @@ -79,6 +79,7 @@ api impls + skipped util diff --git a/src/it/makeAggregateBom/skipped/deploy-config/pom.xml b/src/it/makeAggregateBom/skipped/deploy-config/pom.xml new file mode 100644 index 00000000..c5fa0bcb --- /dev/null +++ b/src/it/makeAggregateBom/skipped/deploy-config/pom.xml @@ -0,0 +1,43 @@ + + + + + 4.0.0 + + + org.cyclonedx.its + skipped + 1.0-SNAPSHOT + + deploy-config + + + + + org.apache.maven.plugins + maven-deploy-plugin + 3.1.1 + + true + + + + + diff --git a/src/it/makeAggregateBom/skipped/deploy-property/pom.xml b/src/it/makeAggregateBom/skipped/deploy-property/pom.xml new file mode 100644 index 00000000..76fbbced --- /dev/null +++ b/src/it/makeAggregateBom/skipped/deploy-property/pom.xml @@ -0,0 +1,34 @@ + + + + + 4.0.0 + + + org.cyclonedx.its + skipped + 1.0-SNAPSHOT + + deploy-property + + + true + + diff --git a/src/it/makeAggregateBom/skipped/nexus-config/pom.xml b/src/it/makeAggregateBom/skipped/nexus-config/pom.xml new file mode 100644 index 00000000..997bef02 --- /dev/null +++ b/src/it/makeAggregateBom/skipped/nexus-config/pom.xml @@ -0,0 +1,48 @@ + + + + + 4.0.0 + + + org.cyclonedx.its + skipped + 1.0-SNAPSHOT + + nexus-config + + + true + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + true + + + + + diff --git a/src/it/makeAggregateBom/skipped/nexus-property/pom.xml b/src/it/makeAggregateBom/skipped/nexus-property/pom.xml new file mode 100644 index 00000000..8ead52d6 --- /dev/null +++ b/src/it/makeAggregateBom/skipped/nexus-property/pom.xml @@ -0,0 +1,45 @@ + + + + + 4.0.0 + + + org.cyclonedx.its + skipped + 1.0-SNAPSHOT + + nexus-property + + + true + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + + + diff --git a/src/it/makeAggregateBom/skipped/pom.xml b/src/it/makeAggregateBom/skipped/pom.xml new file mode 100644 index 00000000..784fab36 --- /dev/null +++ b/src/it/makeAggregateBom/skipped/pom.xml @@ -0,0 +1,38 @@ + + + + + 4.0.0 + + + org.cyclonedx.its + makeAggregateBom + 1.0-SNAPSHOT + + skipped + pom + + + deploy-property + deploy-config + nexus-property + nexus-config + + diff --git a/src/it/makeAggregateBom/verify.groovy b/src/it/makeAggregateBom/verify.groovy index a2e61429..ec101a43 100644 --- a/src/it/makeAggregateBom/verify.groovy +++ b/src/it/makeAggregateBom/verify.groovy @@ -15,25 +15,39 @@ void assertBomFiles(String path, boolean aggregate) { assert bomFileJson.text.contains('"value" : "compile,provided,runtime,system"') } +void assertNoBomFiles(String path) { + File bomFileXml = new File(basedir, path + ".xml") + File bomFileJson = new File(basedir, path + ".json") + + assert !bomFileXml.exists() + assert !bomFileJson.exists() +} + assertBomFiles("target/bom", true) // aggregate assertBomFiles("api/target/bom", false) assertBomFiles("util/target/bom", false) assertBomFiles("impls/target/bom", false) assertBomFiles("impls/impl-A/target/bom", false) assertBomFiles("impls/impl-B/target/bom", false) +assertBomFiles("skipped/target/bom", false) + +assertNoBomFiles("skipped/deploy-config/target/bom") +assertNoBomFiles("skipped/deploy-property/target/bom") +assertNoBomFiles("skipped/nexus-config/target/bom") +assertNoBomFiles("skipped/nexus-property/target/bom") var buildLog = new File(basedir, "build.log").text -assert 11 == (buildLog =~ /\[INFO\] CycloneDX: Resolving Dependencies/).size() +assert 13 == (buildLog =~ /\[INFO\] CycloneDX: Resolving Dependencies/).size() assert 2 == (buildLog =~ /\[INFO\] CycloneDX: Resolving Aggregated Dependencies/).size() -// 13 = 6 modules for main cyclonedx-makeAggregateBom execution +// 15 = 7 modules for main cyclonedx-makeAggregateBom execution // + 1 for root module cyclonedx-makeAggregateBom-root-only execution -// + 6 modules for additional cyclonedx-makeBom execution -assert 13 == (buildLog =~ /\[INFO\] CycloneDX: Writing and validating BOM \(XML\)/).size() -assert 13 == (buildLog =~ /\[INFO\] CycloneDX: Writing and validating BOM \(JSON\)/).size() +// + 7 modules for additional cyclonedx-makeBom execution +assert 15 == (buildLog =~ /\[INFO\] CycloneDX: Writing and validating BOM \(XML\)/).size() +assert 15 == (buildLog =~ /\[INFO\] CycloneDX: Writing and validating BOM \(JSON\)/).size() // cyclonedx-makeAggregateBom-root-only execution skips 5 non-root modules -assert 5 == (buildLog =~ /\[INFO\] Skipping CycloneDX on non-execution root/).size() +assert 6 == (buildLog =~ /\[INFO\] Skipping CycloneDX on non-execution root/).size() // [WARNING] artifact org.cyclonedx.its:api:xml:cyclonedx:1.0-SNAPSHOT already attached, replace previous instance assert 0 == (buildLog =~ /-SNAPSHOT already attached, replace previous instance/).size() @@ -65,4 +79,5 @@ String rootDependencies = bom.substring(bom.indexOf('') assert rootDependencies.contains('') assert rootDependencies.contains('') -assert 4 == (rootDependencies =~ /') +assert 5 == (rootDependencies =~ / topLevelComponents, Map components, Map dependencies) throws MojoExecutionException; + /** + * @return {@literal true} if the execution should be skipped. + */ + protected boolean shouldSkip() { + return Boolean.parseBoolean(System.getProperty("cyclonedx.skip", Boolean.toString(skip))); + } + public void execute() throws MojoExecutionException { - final boolean shouldSkip = Boolean.parseBoolean(System.getProperty("cyclonedx.skip", Boolean.toString(skip))); - if (shouldSkip) { + if (shouldSkip()) { getLog().info("Skipping CycloneDX"); return; } @@ -511,4 +526,44 @@ private Component.Scope mergeScopes(final Component.Scope existing, final Compon } return existing; } + + static boolean isDeployable(final MavenProject project) { + return isDeployable(project, + MAVEN_DEPLOY_PLUGIN, + "skip", + "maven.deploy.skip") + || isDeployable(project, + NEXUS_STAGING_PLUGIN, + "skipNexusStagingDeployMojo", + "skipNexusStagingDeployMojo"); + } + + private static boolean isDeployable(final MavenProject project, + final String pluginKey, + final String parameter, + final String propertyName) { + final Plugin plugin = project.getPlugin(pluginKey); + if (plugin != null) { + // Default skip value + final String property = System.getProperty(propertyName, project.getProperties().getProperty(propertyName)); + final boolean defaultSkipValue = property != null ? Boolean.parseBoolean(property) : false; + // Find an execution that is not skipped + 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()); + } + } + if (!skipValue) { + return true; + } + } + } + } + return false; + } } diff --git a/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java b/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java index 1399a405..7675b403 100644 --- a/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java +++ b/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java @@ -91,6 +91,9 @@ protected boolean shouldExclude(MavenProject mavenProject) { if (excludeTestProject && mavenProject.getArtifactId().contains("test")) { shouldExclude = true; } + if (!BaseCycloneDxMojo.isDeployable(mavenProject)) { + shouldExclude = true; + } return shouldExclude; } @@ -146,7 +149,7 @@ protected String extractComponentsAndDependencies(final Set topLevelComp */ private void addMavenProjectsAsParentDependencies(List reactorProjects, Map dependencies) { for (final MavenProject project: reactorProjects) { - if (project.hasParent()) { + if (project.hasParent() && !shouldExclude(project)) { final String parentRef = generatePackageUrl(project.getParent().getArtifact()); Dependency parentDependency = dependencies.get(parentRef); if (parentDependency != null) { diff --git a/src/main/java/org/cyclonedx/maven/CycloneDxMojo.java b/src/main/java/org/cyclonedx/maven/CycloneDxMojo.java index 1c60def1..44f3b38e 100644 --- a/src/main/java/org/cyclonedx/maven/CycloneDxMojo.java +++ b/src/main/java/org/cyclonedx/maven/CycloneDxMojo.java @@ -94,6 +94,12 @@ protected ProjectDependencyAnalysis doProjectDependencyAnalysis(final MavenProje return null; } + @Override + protected boolean shouldSkip() { + // The list of artifacts would be empty + return super.shouldSkip() || !isDeployable(getProject()); + } + protected String extractComponentsAndDependencies(final Set topLevelComponents, final Map components, final Map dependencies) throws MojoExecutionException { getLog().info(MESSAGE_RESOLVING_DEPS);